@kksdev/ds-angular 1.4.0 → 1.6.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, faMinus, faPlus, faEyeDropper } 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,2738 @@ 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
+
11434
+ class DsInputNumber {
11435
+ // Config
11436
+ min = input(-Infinity, ...(ngDevMode ? [{ debugName: "min" }] : []));
11437
+ max = input(Infinity, ...(ngDevMode ? [{ debugName: "max" }] : []));
11438
+ step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : []));
11439
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
11440
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
11441
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
11442
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
11443
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
11444
+ prefix = input(undefined, ...(ngDevMode ? [{ debugName: "prefix" }] : []));
11445
+ suffix = input(undefined, ...(ngDevMode ? [{ debugName: "suffix" }] : []));
11446
+ precision = input(0, ...(ngDevMode ? [{ debugName: "precision" }] : []));
11447
+ controls = input(true, ...(ngDevMode ? [{ debugName: "controls" }] : []));
11448
+ controlsPosition = input('both', ...(ngDevMode ? [{ debugName: "controlsPosition" }] : []));
11449
+ // Accessibility
11450
+ ariaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
11451
+ id = input(crypto.randomUUID(), ...(ngDevMode ? [{ debugName: "id" }] : []));
11452
+ name = input(undefined, ...(ngDevMode ? [{ debugName: "name" }] : []));
11453
+ // Events
11454
+ valueChange = output();
11455
+ // ViewChild
11456
+ inputElement;
11457
+ // Icons
11458
+ faMinus = faMinus;
11459
+ faPlus = faPlus;
11460
+ // Internal state
11461
+ disabledState = signal(false, ...(ngDevMode ? [{ debugName: "disabledState" }] : []));
11462
+ internalValue = signal(null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
11463
+ focused = signal(false, ...(ngDevMode ? [{ debugName: "focused" }] : []));
11464
+ displayValue = signal('', ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
11465
+ constructor() {
11466
+ // Effect pour formater l'affichage
11467
+ effect(() => {
11468
+ const val = this.internalValue();
11469
+ if (val === null) {
11470
+ this.displayValue.set('');
11471
+ return;
11472
+ }
11473
+ const formatted = this.formatValue(val);
11474
+ this.displayValue.set(formatted);
11475
+ });
11476
+ }
11477
+ isDisabled = computed(() => this.disabled() || this.disabledState(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
11478
+ isReadonly = computed(() => this.readonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : []));
11479
+ showControls = computed(() => this.controls() && !this.isDisabled() && !this.isReadonly(), ...(ngDevMode ? [{ debugName: "showControls" }] : []));
11480
+ controlsOnRight = computed(() => this.controlsPosition() === 'right', ...(ngDevMode ? [{ debugName: "controlsOnRight" }] : []));
11481
+ controlsOnBoth = computed(() => this.controlsPosition() === 'both', ...(ngDevMode ? [{ debugName: "controlsOnBoth" }] : []));
11482
+ containerClasses = computed(() => {
11483
+ const classes = ['ds-input-number'];
11484
+ classes.push(`ds-input-number--${this.size()}`);
11485
+ if (this.isDisabled())
11486
+ classes.push('ds-input-number--disabled');
11487
+ if (this.isReadonly())
11488
+ classes.push('ds-input-number--readonly');
11489
+ if (this.focused())
11490
+ classes.push('ds-input-number--focused');
11491
+ if (this.controlsOnRight())
11492
+ classes.push('ds-input-number--controls-right');
11493
+ if (this.controlsOnBoth())
11494
+ classes.push('ds-input-number--controls-both');
11495
+ if (this.prefix())
11496
+ classes.push('ds-input-number--has-prefix');
11497
+ if (this.suffix())
11498
+ classes.push('ds-input-number--has-suffix');
11499
+ return classes.join(' ');
11500
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
11501
+ canDecrement = computed(() => {
11502
+ if (!this.showControls())
11503
+ return false;
11504
+ const val = this.internalValue();
11505
+ if (val === null)
11506
+ return true;
11507
+ return val > this.min();
11508
+ }, ...(ngDevMode ? [{ debugName: "canDecrement" }] : []));
11509
+ canIncrement = computed(() => {
11510
+ if (!this.showControls())
11511
+ return false;
11512
+ const val = this.internalValue();
11513
+ if (val === null)
11514
+ return true;
11515
+ return val < this.max();
11516
+ }, ...(ngDevMode ? [{ debugName: "canIncrement" }] : []));
11517
+ // ControlValueAccessor
11518
+ onChange = () => { };
11519
+ onTouched = () => { };
11520
+ writeValue(value) {
11521
+ this.internalValue.set(value);
11522
+ }
11523
+ registerOnChange(fn) {
11524
+ this.onChange = fn;
11525
+ }
11526
+ registerOnTouched(fn) {
11527
+ this.onTouched = fn;
11528
+ }
11529
+ setDisabledState(isDisabled) {
11530
+ this.disabledState.set(isDisabled);
11531
+ }
11532
+ // Helpers
11533
+ formatValue(value) {
11534
+ const prec = this.precision();
11535
+ return prec > 0 ? value.toFixed(prec) : Math.floor(value).toString();
11536
+ }
11537
+ parseValue(input) {
11538
+ if (input === '' || input === '-')
11539
+ return null;
11540
+ const parsed = parseFloat(input);
11541
+ if (isNaN(parsed))
11542
+ return null;
11543
+ return parsed;
11544
+ }
11545
+ clampValue(value) {
11546
+ return Math.max(this.min(), Math.min(this.max(), value));
11547
+ }
11548
+ roundToPrecision(value) {
11549
+ const prec = this.precision();
11550
+ if (prec === 0)
11551
+ return Math.round(value);
11552
+ const multiplier = Math.pow(10, prec);
11553
+ return Math.round(value * multiplier) / multiplier;
11554
+ }
11555
+ emitValue(value) {
11556
+ this.onChange(value);
11557
+ if (value !== null) {
11558
+ this.valueChange.emit(value);
11559
+ }
11560
+ }
11561
+ // Event handlers
11562
+ onInputChange(event) {
11563
+ if (this.isDisabled() || this.isReadonly())
11564
+ return;
11565
+ const target = event.target;
11566
+ const parsed = this.parseValue(target.value);
11567
+ if (parsed === null) {
11568
+ this.internalValue.set(null);
11569
+ this.emitValue(null);
11570
+ return;
11571
+ }
11572
+ const clamped = this.clampValue(parsed);
11573
+ const rounded = this.roundToPrecision(clamped);
11574
+ this.internalValue.set(rounded);
11575
+ this.emitValue(rounded);
11576
+ }
11577
+ onInputBlur() {
11578
+ this.focused.set(false);
11579
+ this.onTouched();
11580
+ // Formater la valeur au blur
11581
+ const val = this.internalValue();
11582
+ if (val !== null) {
11583
+ const clamped = this.clampValue(val);
11584
+ const rounded = this.roundToPrecision(clamped);
11585
+ if (rounded !== val) {
11586
+ this.internalValue.set(rounded);
11587
+ this.emitValue(rounded);
11588
+ }
11589
+ }
11590
+ }
11591
+ onInputFocus() {
11592
+ this.focused.set(true);
11593
+ }
11594
+ onKeyDown(event) {
11595
+ if (this.isDisabled() || this.isReadonly())
11596
+ return;
11597
+ const stepVal = this.step();
11598
+ let handled = false;
11599
+ switch (event.key) {
11600
+ case 'ArrowUp':
11601
+ this.increment();
11602
+ handled = true;
11603
+ break;
11604
+ case 'ArrowDown':
11605
+ this.decrement();
11606
+ handled = true;
11607
+ break;
11608
+ case 'Home':
11609
+ this.setToMin();
11610
+ handled = true;
11611
+ break;
11612
+ case 'End':
11613
+ this.setToMax();
11614
+ handled = true;
11615
+ break;
11616
+ case 'PageUp':
11617
+ this.incrementBy(stepVal * 10);
11618
+ handled = true;
11619
+ break;
11620
+ case 'PageDown':
11621
+ this.decrementBy(stepVal * 10);
11622
+ handled = true;
11623
+ break;
11624
+ }
11625
+ if (handled) {
11626
+ event.preventDefault();
11627
+ }
11628
+ }
11629
+ increment() {
11630
+ if (!this.canIncrement())
11631
+ return;
11632
+ this.incrementBy(this.step());
11633
+ }
11634
+ decrement() {
11635
+ if (!this.canDecrement())
11636
+ return;
11637
+ this.decrementBy(this.step());
11638
+ }
11639
+ incrementBy(delta) {
11640
+ const current = this.internalValue() ?? this.min();
11641
+ const newValue = this.clampValue(this.roundToPrecision(current + delta));
11642
+ this.internalValue.set(newValue);
11643
+ this.emitValue(newValue);
11644
+ this.inputElement?.nativeElement.focus();
11645
+ }
11646
+ decrementBy(delta) {
11647
+ const current = this.internalValue() ?? this.max();
11648
+ const newValue = this.clampValue(this.roundToPrecision(current - delta));
11649
+ this.internalValue.set(newValue);
11650
+ this.emitValue(newValue);
11651
+ this.inputElement?.nativeElement.focus();
11652
+ }
11653
+ setToMin() {
11654
+ const newValue = this.min();
11655
+ this.internalValue.set(newValue);
11656
+ this.emitValue(newValue);
11657
+ }
11658
+ setToMax() {
11659
+ const newValue = this.max();
11660
+ this.internalValue.set(newValue);
11661
+ this.emitValue(newValue);
11662
+ }
11663
+ // ARIA helpers
11664
+ ariaValueMin = computed(() => this.min(), ...(ngDevMode ? [{ debugName: "ariaValueMin" }] : []));
11665
+ ariaValueMax = computed(() => this.max(), ...(ngDevMode ? [{ debugName: "ariaValueMax" }] : []));
11666
+ ariaValueNow = computed(() => this.internalValue() ?? undefined, ...(ngDevMode ? [{ debugName: "ariaValueNow" }] : []));
11667
+ ariaValueText = computed(() => {
11668
+ const val = this.internalValue();
11669
+ if (val === null)
11670
+ return undefined;
11671
+ const prefixStr = this.prefix() ?? '';
11672
+ const suffixStr = this.suffix() ?? '';
11673
+ return `${prefixStr}${this.formatValue(val)}${suffixStr}`;
11674
+ }, ...(ngDevMode ? [{ debugName: "ariaValueText" }] : []));
11675
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsInputNumber, deps: [], target: i0.ɵɵFactoryTarget.Component });
11676
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsInputNumber, isStandalone: true, selector: "ds-input-number", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, precision: { classPropertyName: "precision", publicName: "precision", isSignal: true, isRequired: false, transformFunction: null }, controls: { classPropertyName: "controls", publicName: "controls", isSignal: true, isRequired: false, transformFunction: null }, controlsPosition: { classPropertyName: "controlsPosition", publicName: "controlsPosition", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, providers: [
11677
+ {
11678
+ provide: NG_VALUE_ACCESSOR,
11679
+ useExisting: forwardRef(() => DsInputNumber),
11680
+ multi: true,
11681
+ },
11682
+ ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }], ngImport: i0, template: "<div [class]=\"containerClasses()\">\n <!-- Bouton d\u00E9cr\u00E9mentation (position both) -->\n @if (showControls() && controlsOnBoth()) {\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--decrement\"\n [disabled]=\"!canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"'Decrement value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faMinus\" />\n </button>\n }\n\n <!-- Wrapper pour input + prefix/suffix -->\n <div class=\"ds-input-number__input-wrapper\">\n @if (prefix()) {\n <span class=\"ds-input-number__prefix\">{{ prefix() }}</span>\n }\n\n <input\n #inputElement\n type=\"text\"\n class=\"ds-input-number__input\"\n [id]=\"id()\"\n [name]=\"name()\"\n [value]=\"displayValue()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"isReadonly()\"\n [required]=\"required()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-valuemin]=\"ariaValueMin()\"\n [attr.aria-valuemax]=\"ariaValueMax()\"\n [attr.aria-valuenow]=\"ariaValueNow()\"\n [attr.aria-valuetext]=\"ariaValueText()\"\n role=\"spinbutton\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onInputBlur()\"\n (focus)=\"onInputFocus()\"\n (keydown)=\"onKeyDown($event)\"\n />\n\n @if (suffix()) {\n <span class=\"ds-input-number__suffix\">{{ suffix() }}</span>\n }\n </div>\n\n <!-- Boutons position right -->\n @if (showControls() && controlsOnRight()) {\n <div class=\"ds-input-number__controls-right\">\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--increment-right\"\n [disabled]=\"!canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"'Increment value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faPlus\" />\n </button>\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--decrement-right\"\n [disabled]=\"!canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"'Decrement value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faMinus\" />\n </button>\n </div>\n }\n\n <!-- Bouton incr\u00E9mentation (position both) -->\n @if (showControls() && controlsOnBoth()) {\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--increment\"\n [disabled]=\"!canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"'Increment value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faPlus\" />\n </button>\n }\n</div>\n", styles: [".ds-input-number{display:inline-flex;align-items:stretch;position:relative;vertical-align:middle;width:100%;max-width:200px}.ds-input-number__input-wrapper{display:flex;align-items:center;flex:1;position:relative;background-color:var(--input-bg, var(--gray-50));border:1px solid var(--input-border-color, var(--gray-300));border-radius:var(--input-border-radius, var(--radius-2));transition:all .2s ease;overflow:hidden}.ds-input-number--focused .ds-input-number__input-wrapper{border-color:var(--input-focus-border, var(--color-primary));box-shadow:0 0 0 3px var(--input-focus-ring, rgba(37, 99, 235, .1))}.ds-input-number--disabled .ds-input-number__input-wrapper{background-color:var(--input-bg-disabled, var(--gray-100));border-color:var(--input-border-disabled, var(--gray-200));cursor:not-allowed}.ds-input-number--readonly .ds-input-number__input-wrapper{background-color:var(--input-bg-readonly, var(--gray-50));border-color:var(--input-border-color, var(--gray-300))}.ds-input-number__input{flex:1;border:none;outline:none;background:transparent;color:var(--input-text-color, var(--gray-900));font-family:inherit;text-align:center;padding:0 var(--space-2, .5rem)}.ds-input-number__input::placeholder{color:var(--input-placeholder, var(--gray-500))}.ds-input-number__input:disabled{cursor:not-allowed;color:var(--input-text-disabled, var(--gray-500))}.ds-input-number--readonly .ds-input-number__input{cursor:default}.ds-input-number__input::-webkit-outer-spin-button,.ds-input-number__input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.ds-input-number__input[type=number]{-moz-appearance:textfield}.ds-input-number__prefix,.ds-input-number__suffix{display:inline-flex;align-items:center;color:var(--input-placeholder, var(--gray-500));font-size:inherit;padding:0 var(--space-2, .5rem);-webkit-user-select:none;user-select:none}.ds-input-number__prefix{padding-right:0}.ds-input-number__suffix{padding-left:0}.ds-input-number__btn{display:inline-flex;align-items:center;justify-content:center;border:none;outline:none;cursor:pointer;background-color:var(--input-number-btn-bg, var(--gray-100));color:var(--input-text-color, var(--gray-900));transition:all .15s ease;-webkit-user-select:none;user-select:none}.ds-input-number__btn:hover:not(:disabled){background-color:var(--input-number-btn-hover, var(--gray-200))}.ds-input-number__btn:active:not(:disabled){background-color:var(--input-number-btn-active, var(--gray-300))}.ds-input-number__btn:disabled{cursor:not-allowed;background-color:var(--input-number-btn-disabled, var(--gray-50));color:var(--input-text-disabled, var(--gray-400))}.ds-input-number__btn fa-icon{font-size:.875em}.ds-input-number--controls-both .ds-input-number__btn--decrement{border-right:1px solid var(--input-border-color, var(--gray-300));border-top-left-radius:var(--input-border-radius, var(--radius-2));border-bottom-left-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-both .ds-input-number__btn--increment{border-left:1px solid var(--input-border-color, var(--gray-300));border-top-right-radius:var(--input-border-radius, var(--radius-2));border-bottom-right-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-both .ds-input-number__input-wrapper{border-radius:0}.ds-input-number--controls-right .ds-input-number__controls-right{display:flex;flex-direction:column;border-left:1px solid var(--input-border-color, var(--gray-300))}.ds-input-number--controls-right .ds-input-number__btn--increment-right,.ds-input-number--controls-right .ds-input-number__btn--decrement-right{flex:1;width:var(--input-number-btn-size-md, 24px)}.ds-input-number--controls-right .ds-input-number__btn--increment-right{border-bottom:1px solid var(--input-border-color, var(--gray-300));border-top-right-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-right .ds-input-number__btn--decrement-right{border-bottom-right-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-right .ds-input-number__input-wrapper{border-top-right-radius:0;border-bottom-right-radius:0}.ds-input-number--sm{font-size:var(--font-size-sm, .875rem)}.ds-input-number--sm .ds-input-number__input-wrapper{height:var(--input-number-height-sm, 32px)}.ds-input-number--sm .ds-input-number__btn{width:var(--input-number-btn-size-sm, 28px);height:var(--input-number-btn-size-sm, 28px)}.ds-input-number--sm.ds-input-number--controls-right .ds-input-number__btn{width:var(--input-number-btn-size-sm, 20px);height:calc(var(--input-number-height-sm, 32px) / 2)}.ds-input-number--md{font-size:var(--font-size-base, 1rem)}.ds-input-number--md .ds-input-number__input-wrapper{height:var(--input-number-height-md, 40px)}.ds-input-number--md .ds-input-number__btn{width:var(--input-number-btn-size-md, 36px);height:var(--input-number-btn-size-md, 36px)}.ds-input-number--md.ds-input-number--controls-right .ds-input-number__btn{width:var(--input-number-btn-size-md, 24px);height:calc(var(--input-number-height-md, 40px) / 2)}.ds-input-number--lg{font-size:var(--font-size-lg, 1.125rem)}.ds-input-number--lg .ds-input-number__input-wrapper{height:var(--input-number-height-lg, 48px)}.ds-input-number--lg .ds-input-number__btn{width:var(--input-number-btn-size-lg, 44px);height:var(--input-number-btn-size-lg, 44px)}.ds-input-number--lg.ds-input-number--controls-right .ds-input-number__btn{width:var(--input-number-btn-size-lg, 28px);height:calc(var(--input-number-height-lg, 48px) / 2)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: 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"] }] });
11683
+ }
11684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsInputNumber, decorators: [{
11685
+ type: Component,
11686
+ args: [{ selector: 'ds-input-number', imports: [CommonModule, FaIconComponent], providers: [
11687
+ {
11688
+ provide: NG_VALUE_ACCESSOR,
11689
+ useExisting: forwardRef(() => DsInputNumber),
11690
+ multi: true,
11691
+ },
11692
+ ], template: "<div [class]=\"containerClasses()\">\n <!-- Bouton d\u00E9cr\u00E9mentation (position both) -->\n @if (showControls() && controlsOnBoth()) {\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--decrement\"\n [disabled]=\"!canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"'Decrement value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faMinus\" />\n </button>\n }\n\n <!-- Wrapper pour input + prefix/suffix -->\n <div class=\"ds-input-number__input-wrapper\">\n @if (prefix()) {\n <span class=\"ds-input-number__prefix\">{{ prefix() }}</span>\n }\n\n <input\n #inputElement\n type=\"text\"\n class=\"ds-input-number__input\"\n [id]=\"id()\"\n [name]=\"name()\"\n [value]=\"displayValue()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"isReadonly()\"\n [required]=\"required()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-valuemin]=\"ariaValueMin()\"\n [attr.aria-valuemax]=\"ariaValueMax()\"\n [attr.aria-valuenow]=\"ariaValueNow()\"\n [attr.aria-valuetext]=\"ariaValueText()\"\n role=\"spinbutton\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onInputBlur()\"\n (focus)=\"onInputFocus()\"\n (keydown)=\"onKeyDown($event)\"\n />\n\n @if (suffix()) {\n <span class=\"ds-input-number__suffix\">{{ suffix() }}</span>\n }\n </div>\n\n <!-- Boutons position right -->\n @if (showControls() && controlsOnRight()) {\n <div class=\"ds-input-number__controls-right\">\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--increment-right\"\n [disabled]=\"!canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"'Increment value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faPlus\" />\n </button>\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--decrement-right\"\n [disabled]=\"!canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"'Decrement value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faMinus\" />\n </button>\n </div>\n }\n\n <!-- Bouton incr\u00E9mentation (position both) -->\n @if (showControls() && controlsOnBoth()) {\n <button\n type=\"button\"\n class=\"ds-input-number__btn ds-input-number__btn--increment\"\n [disabled]=\"!canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"'Increment value'\"\n tabindex=\"-1\"\n >\n <fa-icon [icon]=\"faPlus\" />\n </button>\n }\n</div>\n", styles: [".ds-input-number{display:inline-flex;align-items:stretch;position:relative;vertical-align:middle;width:100%;max-width:200px}.ds-input-number__input-wrapper{display:flex;align-items:center;flex:1;position:relative;background-color:var(--input-bg, var(--gray-50));border:1px solid var(--input-border-color, var(--gray-300));border-radius:var(--input-border-radius, var(--radius-2));transition:all .2s ease;overflow:hidden}.ds-input-number--focused .ds-input-number__input-wrapper{border-color:var(--input-focus-border, var(--color-primary));box-shadow:0 0 0 3px var(--input-focus-ring, rgba(37, 99, 235, .1))}.ds-input-number--disabled .ds-input-number__input-wrapper{background-color:var(--input-bg-disabled, var(--gray-100));border-color:var(--input-border-disabled, var(--gray-200));cursor:not-allowed}.ds-input-number--readonly .ds-input-number__input-wrapper{background-color:var(--input-bg-readonly, var(--gray-50));border-color:var(--input-border-color, var(--gray-300))}.ds-input-number__input{flex:1;border:none;outline:none;background:transparent;color:var(--input-text-color, var(--gray-900));font-family:inherit;text-align:center;padding:0 var(--space-2, .5rem)}.ds-input-number__input::placeholder{color:var(--input-placeholder, var(--gray-500))}.ds-input-number__input:disabled{cursor:not-allowed;color:var(--input-text-disabled, var(--gray-500))}.ds-input-number--readonly .ds-input-number__input{cursor:default}.ds-input-number__input::-webkit-outer-spin-button,.ds-input-number__input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.ds-input-number__input[type=number]{-moz-appearance:textfield}.ds-input-number__prefix,.ds-input-number__suffix{display:inline-flex;align-items:center;color:var(--input-placeholder, var(--gray-500));font-size:inherit;padding:0 var(--space-2, .5rem);-webkit-user-select:none;user-select:none}.ds-input-number__prefix{padding-right:0}.ds-input-number__suffix{padding-left:0}.ds-input-number__btn{display:inline-flex;align-items:center;justify-content:center;border:none;outline:none;cursor:pointer;background-color:var(--input-number-btn-bg, var(--gray-100));color:var(--input-text-color, var(--gray-900));transition:all .15s ease;-webkit-user-select:none;user-select:none}.ds-input-number__btn:hover:not(:disabled){background-color:var(--input-number-btn-hover, var(--gray-200))}.ds-input-number__btn:active:not(:disabled){background-color:var(--input-number-btn-active, var(--gray-300))}.ds-input-number__btn:disabled{cursor:not-allowed;background-color:var(--input-number-btn-disabled, var(--gray-50));color:var(--input-text-disabled, var(--gray-400))}.ds-input-number__btn fa-icon{font-size:.875em}.ds-input-number--controls-both .ds-input-number__btn--decrement{border-right:1px solid var(--input-border-color, var(--gray-300));border-top-left-radius:var(--input-border-radius, var(--radius-2));border-bottom-left-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-both .ds-input-number__btn--increment{border-left:1px solid var(--input-border-color, var(--gray-300));border-top-right-radius:var(--input-border-radius, var(--radius-2));border-bottom-right-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-both .ds-input-number__input-wrapper{border-radius:0}.ds-input-number--controls-right .ds-input-number__controls-right{display:flex;flex-direction:column;border-left:1px solid var(--input-border-color, var(--gray-300))}.ds-input-number--controls-right .ds-input-number__btn--increment-right,.ds-input-number--controls-right .ds-input-number__btn--decrement-right{flex:1;width:var(--input-number-btn-size-md, 24px)}.ds-input-number--controls-right .ds-input-number__btn--increment-right{border-bottom:1px solid var(--input-border-color, var(--gray-300));border-top-right-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-right .ds-input-number__btn--decrement-right{border-bottom-right-radius:var(--input-border-radius, var(--radius-2))}.ds-input-number--controls-right .ds-input-number__input-wrapper{border-top-right-radius:0;border-bottom-right-radius:0}.ds-input-number--sm{font-size:var(--font-size-sm, .875rem)}.ds-input-number--sm .ds-input-number__input-wrapper{height:var(--input-number-height-sm, 32px)}.ds-input-number--sm .ds-input-number__btn{width:var(--input-number-btn-size-sm, 28px);height:var(--input-number-btn-size-sm, 28px)}.ds-input-number--sm.ds-input-number--controls-right .ds-input-number__btn{width:var(--input-number-btn-size-sm, 20px);height:calc(var(--input-number-height-sm, 32px) / 2)}.ds-input-number--md{font-size:var(--font-size-base, 1rem)}.ds-input-number--md .ds-input-number__input-wrapper{height:var(--input-number-height-md, 40px)}.ds-input-number--md .ds-input-number__btn{width:var(--input-number-btn-size-md, 36px);height:var(--input-number-btn-size-md, 36px)}.ds-input-number--md.ds-input-number--controls-right .ds-input-number__btn{width:var(--input-number-btn-size-md, 24px);height:calc(var(--input-number-height-md, 40px) / 2)}.ds-input-number--lg{font-size:var(--font-size-lg, 1.125rem)}.ds-input-number--lg .ds-input-number__input-wrapper{height:var(--input-number-height-lg, 48px)}.ds-input-number--lg .ds-input-number__btn{width:var(--input-number-btn-size-lg, 44px);height:var(--input-number-btn-size-lg, 44px)}.ds-input-number--lg.ds-input-number--controls-right .ds-input-number__btn{width:var(--input-number-btn-size-lg, 28px);height:calc(var(--input-number-height-lg, 48px) / 2)}\n"] }]
11693
+ }], ctorParameters: () => [], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], precision: [{ type: i0.Input, args: [{ isSignal: true, alias: "precision", required: false }] }], controls: [{ type: i0.Input, args: [{ isSignal: true, alias: "controls", required: false }] }], controlsPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlsPosition", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], inputElement: [{
11694
+ type: ViewChild,
11695
+ args: ['inputElement', { static: false }]
11696
+ }] } });
11697
+
11698
+ /**
11699
+ * DS Segmented Control
11700
+ *
11701
+ * Composant groupe de boutons mutuellement exclusifs visuellement connectés.
11702
+ * Alternative stylisée aux boutons radio pour basculer entre vues, modes ou options.
11703
+ *
11704
+ * @example
11705
+ * // Formulaire réactif
11706
+ * <ds-segmented-control
11707
+ * formControlName="view"
11708
+ * [options]="viewOptions"
11709
+ * size="md"
11710
+ * fullWidth
11711
+ * />
11712
+ *
11713
+ * @example
11714
+ * // Template-driven
11715
+ * <ds-segmented-control
11716
+ * [(ngModel)]="selectedView"
11717
+ * [options]="[
11718
+ * { value: 'list', label: 'Liste', icon: 'fas-list' },
11719
+ * { value: 'grid', label: 'Grille', icon: 'fas-grid' },
11720
+ * { value: 'map', label: 'Carte', icon: 'fas-map' }
11721
+ * ]"
11722
+ * color="neutral"
11723
+ * />
11724
+ */
11725
+ class DsSegmentedControl {
11726
+ /**
11727
+ * Liste des options du segmented control
11728
+ */
11729
+ options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
11730
+ /**
11731
+ * Taille du segmented control (sm, md, lg)
11732
+ */
11733
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
11734
+ /**
11735
+ * État désactivé global
11736
+ */
11737
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
11738
+ /**
11739
+ * Prend toute la largeur du conteneur
11740
+ */
11741
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : []));
11742
+ /**
11743
+ * Orientation (horizontal, vertical)
11744
+ */
11745
+ orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
11746
+ /**
11747
+ * Couleur du segment actif (primary, neutral)
11748
+ */
11749
+ color = input('primary', ...(ngDevMode ? [{ debugName: "color" }] : []));
11750
+ // Internal state
11751
+ disabledState = signal(false, ...(ngDevMode ? [{ debugName: "disabledState" }] : []));
11752
+ internalValue = signal(null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
11753
+ // Computed properties
11754
+ isDisabled = computed(() => this.disabled() || this.disabledState(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
11755
+ containerClasses = computed(() => ({
11756
+ 'ds-segmented-control': true,
11757
+ [`ds-segmented-control--${this.size()}`]: true,
11758
+ [`ds-segmented-control--${this.orientation()}`]: true,
11759
+ [`ds-segmented-control--${this.color()}`]: true,
11760
+ 'ds-segmented-control--full-width': this.fullWidth(),
11761
+ 'ds-segmented-control--disabled': this.isDisabled(),
11762
+ }), ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
11763
+ activeIndex = computed(() => {
11764
+ const value = this.internalValue();
11765
+ if (!value)
11766
+ return -1;
11767
+ return this.options().findIndex((opt) => opt.value === value);
11768
+ }, ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
11769
+ /**
11770
+ * Vérifie si une option est sélectionnée
11771
+ */
11772
+ isOptionSelected(option) {
11773
+ return this.internalValue() === option.value;
11774
+ }
11775
+ /**
11776
+ * Vérifie si une option est désactivée
11777
+ */
11778
+ isOptionDisabled(option) {
11779
+ return this.isDisabled() || !!option.disabled;
11780
+ }
11781
+ /**
11782
+ * Classes CSS pour un segment
11783
+ */
11784
+ getSegmentClasses(option) {
11785
+ return {
11786
+ 'ds-segmented-control__segment': true,
11787
+ 'ds-segmented-control__segment--active': this.isOptionSelected(option),
11788
+ 'ds-segmented-control__segment--disabled': this.isOptionDisabled(option),
11789
+ };
11790
+ }
11791
+ // ControlValueAccessor implementation
11792
+ onChange = () => { };
11793
+ onTouched = () => { };
11794
+ writeValue(value) {
11795
+ this.internalValue.set(value);
11796
+ }
11797
+ registerOnChange(fn) {
11798
+ this.onChange = fn;
11799
+ }
11800
+ registerOnTouched(fn) {
11801
+ this.onTouched = fn;
11802
+ }
11803
+ setDisabledState(isDisabled) {
11804
+ this.disabledState.set(isDisabled);
11805
+ }
11806
+ // Event handlers
11807
+ onSegmentClick(option) {
11808
+ if (this.isOptionDisabled(option)) {
11809
+ return;
11810
+ }
11811
+ this.internalValue.set(option.value);
11812
+ this.onChange(option.value);
11813
+ this.onTouched();
11814
+ }
11815
+ onKeydown(event) {
11816
+ const target = event.target;
11817
+ if (target.getAttribute('role') !== 'radio')
11818
+ return;
11819
+ const currentIndex = this.activeIndex();
11820
+ const optionsArray = this.options();
11821
+ const isHorizontal = this.orientation() === 'horizontal';
11822
+ let nextIndex = currentIndex;
11823
+ switch (event.key) {
11824
+ case 'ArrowLeft':
11825
+ if (!isHorizontal)
11826
+ return;
11827
+ event.preventDefault();
11828
+ nextIndex = this.findPreviousEnabledOption(currentIndex);
11829
+ break;
11830
+ case 'ArrowRight':
11831
+ if (!isHorizontal)
11832
+ return;
11833
+ event.preventDefault();
11834
+ nextIndex = this.findNextEnabledOption(currentIndex);
11835
+ break;
11836
+ case 'ArrowUp':
11837
+ if (isHorizontal)
11838
+ return;
11839
+ event.preventDefault();
11840
+ nextIndex = this.findPreviousEnabledOption(currentIndex);
11841
+ break;
11842
+ case 'ArrowDown':
11843
+ if (isHorizontal)
11844
+ return;
11845
+ event.preventDefault();
11846
+ nextIndex = this.findNextEnabledOption(currentIndex);
11847
+ break;
11848
+ case 'Home':
11849
+ event.preventDefault();
11850
+ nextIndex = this.findNextEnabledOption(-1);
11851
+ break;
11852
+ case 'End':
11853
+ event.preventDefault();
11854
+ nextIndex = this.findPreviousEnabledOption(optionsArray.length);
11855
+ break;
11856
+ default:
11857
+ return;
11858
+ }
11859
+ if (nextIndex !== currentIndex && nextIndex !== -1) {
11860
+ this.onSegmentClick(optionsArray[nextIndex]);
11861
+ this.focusSegment(nextIndex);
11862
+ }
11863
+ }
11864
+ findNextEnabledOption(startIndex) {
11865
+ const optionsArray = this.options();
11866
+ for (let i = startIndex + 1; i < optionsArray.length; i++) {
11867
+ if (!optionsArray[i].disabled)
11868
+ return i;
11869
+ }
11870
+ return startIndex === -1 ? 0 : startIndex;
11871
+ }
11872
+ findPreviousEnabledOption(startIndex) {
11873
+ const optionsArray = this.options();
11874
+ for (let i = startIndex - 1; i >= 0; i--) {
11875
+ if (!optionsArray[i].disabled)
11876
+ return i;
11877
+ }
11878
+ return startIndex === optionsArray.length ? optionsArray.length - 1 : startIndex;
11879
+ }
11880
+ focusSegment(index) {
11881
+ setTimeout(() => {
11882
+ const segment = document.querySelector(`.ds-segmented-control__segment[data-index="${index}"]`);
11883
+ segment?.focus();
11884
+ }, 0);
11885
+ }
11886
+ // Track function for @for
11887
+ trackByValue(_index, option) {
11888
+ return option.value;
11889
+ }
11890
+ /**
11891
+ * Parse l'icône depuis le format string vers le format FontAwesome
11892
+ * @param iconName - Nom de l'icône (ex: 'fas-list', 'list', 'fa-solid fa-list')
11893
+ * @returns Tuple [prefix, iconName] pour FontAwesome
11894
+ */
11895
+ parseIcon(iconName) {
11896
+ // Format: 'fas-list' ou 'far-check'
11897
+ if (iconName.includes('-')) {
11898
+ const parts = iconName.split('-');
11899
+ if (parts[0] === 'fas' || parts[0] === 'far' || parts[0] === 'fab') {
11900
+ return [parts[0], parts.slice(1).join('-')];
11901
+ }
11902
+ }
11903
+ // Par défaut, utiliser 'fas' comme préfixe
11904
+ return ['fas', iconName];
11905
+ }
11906
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsSegmentedControl, deps: [], target: i0.ɵɵFactoryTarget.Component });
11907
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsSegmentedControl, isStandalone: true, selector: "ds-segmented-control", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)" } }, providers: [
11908
+ {
11909
+ provide: NG_VALUE_ACCESSOR,
11910
+ useExisting: forwardRef(() => DsSegmentedControl),
11911
+ multi: true,
11912
+ },
11913
+ ], ngImport: i0, template: "<div\n [ngClass]=\"containerClasses()\"\n role=\"radiogroup\"\n [attr.aria-disabled]=\"isDisabled()\"\n>\n @for (option of options(); track trackByValue($index, option)) {\n <button\n type=\"button\"\n role=\"radio\"\n [ngClass]=\"getSegmentClasses(option)\"\n [attr.data-index]=\"$index\"\n [attr.aria-checked]=\"isOptionSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option)\"\n [disabled]=\"isOptionDisabled(option)\"\n [tabindex]=\"isOptionSelected(option) ? 0 : -1\"\n (click)=\"onSegmentClick(option)\"\n >\n @if (option.icon) {\n <fa-icon\n class=\"ds-segmented-control__icon\"\n [icon]=\"parseIcon(option.icon)\"\n />\n }\n <span class=\"ds-segmented-control__label\">{{ option.label }}</span>\n </button>\n }\n</div>\n", styles: [".ds-segmented-control{display:inline-flex;background:var(--segmented-bg);border-radius:var(--segmented-border-radius);padding:var(--segmented-gap);gap:var(--segmented-gap);box-shadow:inset 0 0 0 1px var(--segmented-border-color)}.ds-segmented-control--horizontal{flex-direction:row}.ds-segmented-control--vertical{flex-direction:column}.ds-segmented-control--full-width{display:flex;width:100%}.ds-segmented-control--full-width .ds-segmented-control__segment{flex:1}.ds-segmented-control--disabled{opacity:.5;cursor:not-allowed}.ds-segmented-control--sm .ds-segmented-control__segment{height:var(--segmented-height-sm);padding:0 var(--segmented-padding-sm);font-size:var(--segmented-font-size-sm);border-radius:calc(var(--segmented-border-radius) - var(--segmented-gap))}.ds-segmented-control--sm .ds-segmented-control__icon{font-size:var(--segmented-icon-size-sm)}.ds-segmented-control--md .ds-segmented-control__segment{height:var(--segmented-height-md);padding:0 var(--segmented-padding-md);font-size:var(--segmented-font-size-md);border-radius:calc(var(--segmented-border-radius) - var(--segmented-gap))}.ds-segmented-control--md .ds-segmented-control__icon{font-size:var(--segmented-icon-size-md)}.ds-segmented-control--lg .ds-segmented-control__segment{height:var(--segmented-height-lg);padding:0 var(--segmented-padding-lg);font-size:var(--segmented-font-size-lg);border-radius:calc(var(--segmented-border-radius) - var(--segmented-gap))}.ds-segmented-control--lg .ds-segmented-control__icon{font-size:var(--segmented-icon-size-lg)}.ds-segmented-control__segment{display:inline-flex;align-items:center;justify-content:center;gap:var(--segmented-segment-gap);border:none;background:transparent;color:var(--segmented-text);font-weight:500;cursor:pointer;transition:all .2s ease;white-space:nowrap;-webkit-user-select:none;user-select:none}.ds-segmented-control__segment:hover:not(.ds-segmented-control__segment--disabled):not(.ds-segmented-control__segment--active){background:var(--segmented-hover-bg)}.ds-segmented-control__segment:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.ds-segmented-control__segment--active{background:var(--segmented-active-bg);color:var(--segmented-active-text);box-shadow:var(--segmented-active-shadow);font-weight:600}.ds-segmented-control__segment--disabled{cursor:not-allowed;opacity:.4}.ds-segmented-control__icon{display:inline-flex;flex-shrink:0}.ds-segmented-control__label{display:inline-flex}.ds-segmented-control--primary .ds-segmented-control__segment--active{background:var(--segmented-active-bg-primary);color:var(--segmented-active-text-primary)}.ds-segmented-control--neutral .ds-segmented-control__segment--active{background:var(--segmented-active-bg-neutral);color:var(--segmented-active-text-neutral)}\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"] }] });
11914
+ }
11915
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsSegmentedControl, decorators: [{
11916
+ type: Component,
11917
+ args: [{ selector: 'ds-segmented-control', standalone: true, imports: [CommonModule, FontAwesomeModule], providers: [
11918
+ {
11919
+ provide: NG_VALUE_ACCESSOR,
11920
+ useExisting: forwardRef(() => DsSegmentedControl),
11921
+ multi: true,
11922
+ },
11923
+ ], template: "<div\n [ngClass]=\"containerClasses()\"\n role=\"radiogroup\"\n [attr.aria-disabled]=\"isDisabled()\"\n>\n @for (option of options(); track trackByValue($index, option)) {\n <button\n type=\"button\"\n role=\"radio\"\n [ngClass]=\"getSegmentClasses(option)\"\n [attr.data-index]=\"$index\"\n [attr.aria-checked]=\"isOptionSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option)\"\n [disabled]=\"isOptionDisabled(option)\"\n [tabindex]=\"isOptionSelected(option) ? 0 : -1\"\n (click)=\"onSegmentClick(option)\"\n >\n @if (option.icon) {\n <fa-icon\n class=\"ds-segmented-control__icon\"\n [icon]=\"parseIcon(option.icon)\"\n />\n }\n <span class=\"ds-segmented-control__label\">{{ option.label }}</span>\n </button>\n }\n</div>\n", styles: [".ds-segmented-control{display:inline-flex;background:var(--segmented-bg);border-radius:var(--segmented-border-radius);padding:var(--segmented-gap);gap:var(--segmented-gap);box-shadow:inset 0 0 0 1px var(--segmented-border-color)}.ds-segmented-control--horizontal{flex-direction:row}.ds-segmented-control--vertical{flex-direction:column}.ds-segmented-control--full-width{display:flex;width:100%}.ds-segmented-control--full-width .ds-segmented-control__segment{flex:1}.ds-segmented-control--disabled{opacity:.5;cursor:not-allowed}.ds-segmented-control--sm .ds-segmented-control__segment{height:var(--segmented-height-sm);padding:0 var(--segmented-padding-sm);font-size:var(--segmented-font-size-sm);border-radius:calc(var(--segmented-border-radius) - var(--segmented-gap))}.ds-segmented-control--sm .ds-segmented-control__icon{font-size:var(--segmented-icon-size-sm)}.ds-segmented-control--md .ds-segmented-control__segment{height:var(--segmented-height-md);padding:0 var(--segmented-padding-md);font-size:var(--segmented-font-size-md);border-radius:calc(var(--segmented-border-radius) - var(--segmented-gap))}.ds-segmented-control--md .ds-segmented-control__icon{font-size:var(--segmented-icon-size-md)}.ds-segmented-control--lg .ds-segmented-control__segment{height:var(--segmented-height-lg);padding:0 var(--segmented-padding-lg);font-size:var(--segmented-font-size-lg);border-radius:calc(var(--segmented-border-radius) - var(--segmented-gap))}.ds-segmented-control--lg .ds-segmented-control__icon{font-size:var(--segmented-icon-size-lg)}.ds-segmented-control__segment{display:inline-flex;align-items:center;justify-content:center;gap:var(--segmented-segment-gap);border:none;background:transparent;color:var(--segmented-text);font-weight:500;cursor:pointer;transition:all .2s ease;white-space:nowrap;-webkit-user-select:none;user-select:none}.ds-segmented-control__segment:hover:not(.ds-segmented-control__segment--disabled):not(.ds-segmented-control__segment--active){background:var(--segmented-hover-bg)}.ds-segmented-control__segment:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.ds-segmented-control__segment--active{background:var(--segmented-active-bg);color:var(--segmented-active-text);box-shadow:var(--segmented-active-shadow);font-weight:600}.ds-segmented-control__segment--disabled{cursor:not-allowed;opacity:.4}.ds-segmented-control__icon{display:inline-flex;flex-shrink:0}.ds-segmented-control__label{display:inline-flex}.ds-segmented-control--primary .ds-segmented-control__segment--active{background:var(--segmented-active-bg-primary);color:var(--segmented-active-text-primary)}.ds-segmented-control--neutral .ds-segmented-control__segment--active{background:var(--segmented-active-bg-neutral);color:var(--segmented-active-text-neutral)}\n"] }]
11924
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], onKeydown: [{
11925
+ type: HostListener,
11926
+ args: ['keydown', ['$event']]
11927
+ }] } });
11928
+
11929
+ class DsColorPickerPanelComponent {
11930
+ spectrumCanvas;
11931
+ // Config (set by parent)
11932
+ value = '';
11933
+ showAlpha = false;
11934
+ presetColors = [];
11935
+ recentColors = [];
11936
+ showRecentColors = false;
11937
+ format = 'hex';
11938
+ // Outputs
11939
+ colorSelected = output();
11940
+ closed = output();
11941
+ // Icons
11942
+ checkIcon = faCheck;
11943
+ // State
11944
+ hue = signal(0, ...(ngDevMode ? [{ debugName: "hue" }] : []));
11945
+ saturation = signal(100, ...(ngDevMode ? [{ debugName: "saturation" }] : []));
11946
+ lightness = signal(50, ...(ngDevMode ? [{ debugName: "lightness" }] : []));
11947
+ alpha = signal(1, ...(ngDevMode ? [{ debugName: "alpha" }] : []));
11948
+ cursorX = signal(null, ...(ngDevMode ? [{ debugName: "cursorX" }] : []));
11949
+ cursorY = signal(null, ...(ngDevMode ? [{ debugName: "cursorY" }] : []));
11950
+ isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : []));
11951
+ // Computed
11952
+ rgb = computed(() => this.hslToRGB(this.hue(), this.saturation(), this.lightness()), ...(ngDevMode ? [{ debugName: "rgb" }] : []));
11953
+ hexValue = computed(() => {
11954
+ const rgb = this.rgb();
11955
+ const r = rgb.r.toString(16).padStart(2, '0');
11956
+ const g = rgb.g.toString(16).padStart(2, '0');
11957
+ const b = rgb.b.toString(16).padStart(2, '0');
11958
+ return `#${r}${g}${b}`;
11959
+ }, ...(ngDevMode ? [{ debugName: "hexValue" }] : []));
11960
+ currentColor = computed(() => {
11961
+ if (this.showAlpha && this.alpha() < 1) {
11962
+ const rgb = this.rgb();
11963
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${this.alpha()})`;
11964
+ }
11965
+ return this.hexValue();
11966
+ }, ...(ngDevMode ? [{ debugName: "currentColor" }] : []));
11967
+ constructor() {
11968
+ // Observer les changements de couleur
11969
+ effect(() => {
11970
+ const color = this.currentColor();
11971
+ if (color !== this.value) {
11972
+ this.colorSelected.emit(color);
11973
+ }
11974
+ });
11975
+ }
11976
+ ngAfterViewInit() {
11977
+ this.initializeFromValue();
11978
+ this.renderSpectrum();
11979
+ }
11980
+ initializeFromValue() {
11981
+ if (!this.value) {
11982
+ this.updateCursor();
11983
+ return;
11984
+ }
11985
+ const rgb = this.parseColor(this.value);
11986
+ if (rgb) {
11987
+ const hsl = this.rgbToHSL(rgb.r, rgb.g, rgb.b);
11988
+ this.hue.set(hsl.h);
11989
+ this.saturation.set(hsl.s);
11990
+ this.lightness.set(hsl.l);
11991
+ if (rgb.a !== undefined) {
11992
+ this.alpha.set(rgb.a);
11993
+ }
11994
+ this.updateCursor();
11995
+ }
11996
+ }
11997
+ renderSpectrum() {
11998
+ if (!this.spectrumCanvas)
11999
+ return;
12000
+ const canvas = this.spectrumCanvas.nativeElement;
12001
+ const ctx = canvas.getContext('2d');
12002
+ if (!ctx)
12003
+ return;
12004
+ const width = canvas.width;
12005
+ const height = canvas.height;
12006
+ // Gradient de saturation (horizontal)
12007
+ const satGradient = ctx.createLinearGradient(0, 0, width, 0);
12008
+ satGradient.addColorStop(0, 'white');
12009
+ satGradient.addColorStop(1, `hsl(${this.hue()}, 100%, 50%)`);
12010
+ ctx.fillStyle = satGradient;
12011
+ ctx.fillRect(0, 0, width, height);
12012
+ // Gradient de luminosité (vertical)
12013
+ const lightGradient = ctx.createLinearGradient(0, 0, 0, height);
12014
+ lightGradient.addColorStop(0, 'transparent');
12015
+ lightGradient.addColorStop(1, 'black');
12016
+ ctx.fillStyle = lightGradient;
12017
+ ctx.fillRect(0, 0, width, height);
12018
+ }
12019
+ updateCursor() {
12020
+ if (!this.spectrumCanvas)
12021
+ return;
12022
+ const canvas = this.spectrumCanvas.nativeElement;
12023
+ const x = (this.saturation() / 100) * canvas.width;
12024
+ const y = ((100 - this.lightness()) / 100) * canvas.height;
12025
+ this.cursorX.set(x);
12026
+ this.cursorY.set(y);
12027
+ }
12028
+ onHueChange(event) {
12029
+ const target = event.target;
12030
+ this.hue.set(parseInt(target.value, 10));
12031
+ this.renderSpectrum();
12032
+ }
12033
+ onAlphaChange(event) {
12034
+ const target = event.target;
12035
+ this.alpha.set(parseInt(target.value, 10) / 100);
12036
+ }
12037
+ onSpectrumMouseDown(event) {
12038
+ this.isDragging.set(true);
12039
+ this.updateColorFromSpectrum(event);
12040
+ }
12041
+ onSpectrumMouseMove(event) {
12042
+ if (this.isDragging()) {
12043
+ this.updateColorFromSpectrum(event);
12044
+ }
12045
+ }
12046
+ onSpectrumMouseUp() {
12047
+ this.isDragging.set(false);
12048
+ }
12049
+ updateColorFromSpectrum(event) {
12050
+ if (!this.spectrumCanvas)
12051
+ return;
12052
+ const canvas = this.spectrumCanvas.nativeElement;
12053
+ const rect = canvas.getBoundingClientRect();
12054
+ const x = Math.max(0, Math.min(event.clientX - rect.left, canvas.width));
12055
+ const y = Math.max(0, Math.min(event.clientY - rect.top, canvas.height));
12056
+ const saturation = (x / canvas.width) * 100;
12057
+ const lightness = 100 - (y / canvas.height) * 100;
12058
+ this.saturation.set(Math.round(saturation));
12059
+ this.lightness.set(Math.round(lightness));
12060
+ this.cursorX.set(x);
12061
+ this.cursorY.set(y);
12062
+ }
12063
+ onHexInput(event) {
12064
+ const target = event.target;
12065
+ const hex = target.value;
12066
+ if (!hex.startsWith('#')) {
12067
+ target.value = '#' + hex;
12068
+ return;
12069
+ }
12070
+ const rgb = this.parseColor(hex);
12071
+ if (rgb) {
12072
+ const hsl = this.rgbToHSL(rgb.r, rgb.g, rgb.b);
12073
+ this.hue.set(hsl.h);
12074
+ this.saturation.set(hsl.s);
12075
+ this.lightness.set(hsl.l);
12076
+ this.renderSpectrum();
12077
+ this.updateCursor();
12078
+ }
12079
+ }
12080
+ onRGBInput(channel, event) {
12081
+ const target = event.target;
12082
+ const value = parseInt(target.value, 10);
12083
+ if (isNaN(value) || value < 0 || value > 255)
12084
+ return;
12085
+ const currentRGB = this.rgb();
12086
+ const newRGB = { ...currentRGB, [channel]: value };
12087
+ const hsl = this.rgbToHSL(newRGB.r, newRGB.g, newRGB.b);
12088
+ this.hue.set(hsl.h);
12089
+ this.saturation.set(hsl.s);
12090
+ this.lightness.set(hsl.l);
12091
+ this.renderSpectrum();
12092
+ this.updateCursor();
12093
+ }
12094
+ onAlphaInputManual(event) {
12095
+ const target = event.target;
12096
+ const value = parseFloat(target.value);
12097
+ if (isNaN(value) || value < 0 || value > 1)
12098
+ return;
12099
+ this.alpha.set(value);
12100
+ }
12101
+ selectPreset(color) {
12102
+ const rgb = this.parseColor(color);
12103
+ if (rgb) {
12104
+ const hsl = this.rgbToHSL(rgb.r, rgb.g, rgb.b);
12105
+ this.hue.set(hsl.h);
12106
+ this.saturation.set(hsl.s);
12107
+ this.lightness.set(hsl.l);
12108
+ if (rgb.a !== undefined) {
12109
+ this.alpha.set(rgb.a);
12110
+ }
12111
+ this.renderSpectrum();
12112
+ this.updateCursor();
12113
+ }
12114
+ }
12115
+ // === Conversions de couleurs ===
12116
+ hslToRGB(h, s, l) {
12117
+ h = h / 360;
12118
+ s = s / 100;
12119
+ l = l / 100;
12120
+ let r, g, b;
12121
+ if (s === 0) {
12122
+ r = g = b = l;
12123
+ }
12124
+ else {
12125
+ const hue2rgb = (p, q, t) => {
12126
+ if (t < 0)
12127
+ t += 1;
12128
+ if (t > 1)
12129
+ t -= 1;
12130
+ if (t < 1 / 6)
12131
+ return p + (q - p) * 6 * t;
12132
+ if (t < 1 / 2)
12133
+ return q;
12134
+ if (t < 2 / 3)
12135
+ return p + (q - p) * (2 / 3 - t) * 6;
12136
+ return p;
12137
+ };
12138
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
12139
+ const p = 2 * l - q;
12140
+ r = hue2rgb(p, q, h + 1 / 3);
12141
+ g = hue2rgb(p, q, h);
12142
+ b = hue2rgb(p, q, h - 1 / 3);
12143
+ }
12144
+ return {
12145
+ r: Math.round(r * 255),
12146
+ g: Math.round(g * 255),
12147
+ b: Math.round(b * 255),
12148
+ };
12149
+ }
12150
+ rgbToHSL(r, g, b) {
12151
+ r /= 255;
12152
+ g /= 255;
12153
+ b /= 255;
12154
+ const max = Math.max(r, g, b);
12155
+ const min = Math.min(r, g, b);
12156
+ let h = 0;
12157
+ let s = 0;
12158
+ const l = (max + min) / 2;
12159
+ if (max !== min) {
12160
+ const d = max - min;
12161
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
12162
+ switch (max) {
12163
+ case r:
12164
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
12165
+ break;
12166
+ case g:
12167
+ h = ((b - r) / d + 2) / 6;
12168
+ break;
12169
+ case b:
12170
+ h = ((r - g) / d + 4) / 6;
12171
+ break;
12172
+ }
12173
+ }
12174
+ return {
12175
+ h: Math.round(h * 360),
12176
+ s: Math.round(s * 100),
12177
+ l: Math.round(l * 100),
12178
+ };
12179
+ }
12180
+ parseColor(color) {
12181
+ // Parse HEX
12182
+ if (color.startsWith('#')) {
12183
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(color);
12184
+ if (!result)
12185
+ return null;
12186
+ return {
12187
+ r: parseInt(result[1], 16),
12188
+ g: parseInt(result[2], 16),
12189
+ b: parseInt(result[3], 16),
12190
+ a: result[4] ? parseInt(result[4], 16) / 255 : undefined,
12191
+ };
12192
+ }
12193
+ // Parse RGB/RGBA
12194
+ if (color.startsWith('rgb')) {
12195
+ const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
12196
+ if (!match)
12197
+ return null;
12198
+ return {
12199
+ r: parseInt(match[1], 10),
12200
+ g: parseInt(match[2], 10),
12201
+ b: parseInt(match[3], 10),
12202
+ a: match[4] ? parseFloat(match[4]) : undefined,
12203
+ };
12204
+ }
12205
+ return null;
12206
+ }
12207
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsColorPickerPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12208
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsColorPickerPanelComponent, isStandalone: true, selector: "ds-color-picker-panel", outputs: { colorSelected: "colorSelected", closed: "closed" }, viewQueries: [{ propertyName: "spectrumCanvas", first: true, predicate: ["spectrumCanvas"], descendants: true }], ngImport: i0, template: `
12209
+ <div class="ds-color-picker-panel" role="dialog" aria-label="Color picker">
12210
+ <!-- Spectre de couleurs -->
12211
+ <div class="ds-color-picker-panel__spectrum">
12212
+ <canvas
12213
+ #spectrumCanvas
12214
+ class="ds-color-picker-panel__spectrum-canvas"
12215
+ width="280"
12216
+ height="180"
12217
+ (mousedown)="onSpectrumMouseDown($event)"
12218
+ (mousemove)="onSpectrumMouseMove($event)"
12219
+ (mouseup)="onSpectrumMouseUp()"
12220
+ (mouseleave)="onSpectrumMouseUp()">
12221
+ </canvas>
12222
+
12223
+ <!-- Curseur de sélection -->
12224
+ @if (cursorX() !== null && cursorY() !== null) {
12225
+ <div
12226
+ class="ds-color-picker-panel__cursor"
12227
+ [style.left.px]="cursorX()"
12228
+ [style.top.px]="cursorY()">
12229
+ </div>
12230
+ }
12231
+ </div>
12232
+
12233
+ <!-- Slider de teinte -->
12234
+ <div class="ds-color-picker-panel__hue-slider">
12235
+ <input
12236
+ type="range"
12237
+ class="ds-color-picker-panel__slider"
12238
+ min="0"
12239
+ max="360"
12240
+ step="1"
12241
+ [value]="hue()"
12242
+ (input)="onHueChange($event)"
12243
+ aria-label="Hue" />
12244
+ </div>
12245
+
12246
+ <!-- Slider alpha (optionnel) -->
12247
+ @if (showAlpha) {
12248
+ <div class="ds-color-picker-panel__alpha-slider">
12249
+ <div class="ds-color-picker-panel__alpha-bg"></div>
12250
+ <input
12251
+ type="range"
12252
+ class="ds-color-picker-panel__slider ds-color-picker-panel__slider--alpha"
12253
+ min="0"
12254
+ max="100"
12255
+ step="1"
12256
+ [value]="alpha() * 100"
12257
+ (input)="onAlphaChange($event)"
12258
+ aria-label="Alpha" />
12259
+ </div>
12260
+ }
12261
+
12262
+ <!-- Inputs manuels -->
12263
+ <div class="ds-color-picker-panel__inputs">
12264
+ @if (format === 'hex') {
12265
+ <div class="ds-color-picker-panel__input-group">
12266
+ <label class="ds-color-picker-panel__label">HEX</label>
12267
+ <input
12268
+ type="text"
12269
+ class="ds-color-picker-panel__input"
12270
+ [value]="hexValue()"
12271
+ (input)="onHexInput($event)"
12272
+ maxlength="7"
12273
+ placeholder="#000000" />
12274
+ </div>
12275
+ }
12276
+
12277
+ @if (format === 'rgb') {
12278
+ <div class="ds-color-picker-panel__input-group">
12279
+ <label class="ds-color-picker-panel__label">R</label>
12280
+ <input
12281
+ type="number"
12282
+ class="ds-color-picker-panel__input"
12283
+ min="0"
12284
+ max="255"
12285
+ [value]="rgb().r"
12286
+ (input)="onRGBInput('r', $event)" />
12287
+ </div>
12288
+ <div class="ds-color-picker-panel__input-group">
12289
+ <label class="ds-color-picker-panel__label">G</label>
12290
+ <input
12291
+ type="number"
12292
+ class="ds-color-picker-panel__input"
12293
+ min="0"
12294
+ max="255"
12295
+ [value]="rgb().g"
12296
+ (input)="onRGBInput('g', $event)" />
12297
+ </div>
12298
+ <div class="ds-color-picker-panel__input-group">
12299
+ <label class="ds-color-picker-panel__label">B</label>
12300
+ <input
12301
+ type="number"
12302
+ class="ds-color-picker-panel__input"
12303
+ min="0"
12304
+ max="255"
12305
+ [value]="rgb().b"
12306
+ (input)="onRGBInput('b', $event)" />
12307
+ </div>
12308
+ @if (showAlpha) {
12309
+ <div class="ds-color-picker-panel__input-group">
12310
+ <label class="ds-color-picker-panel__label">A</label>
12311
+ <input
12312
+ type="number"
12313
+ class="ds-color-picker-panel__input"
12314
+ min="0"
12315
+ max="1"
12316
+ step="0.01"
12317
+ [value]="alpha()"
12318
+ (input)="onAlphaInputManual($event)" />
12319
+ </div>
12320
+ }
12321
+ }
12322
+ </div>
12323
+
12324
+ <!-- Couleurs prédéfinies -->
12325
+ @if (presetColors.length > 0) {
12326
+ <div class="ds-color-picker-panel__presets">
12327
+ <div class="ds-color-picker-panel__presets-label">Presets</div>
12328
+ <div class="ds-color-picker-panel__presets-grid">
12329
+ @for (color of presetColors; track color) {
12330
+ <button
12331
+ type="button"
12332
+ class="ds-color-picker-panel__preset"
12333
+ [style.background-color]="color"
12334
+ [class.ds-color-picker-panel__preset--selected]="color === value"
12335
+ (click)="selectPreset(color)"
12336
+ [attr.aria-label]="'Select ' + color">
12337
+ @if (color === value) {
12338
+ <fa-icon [icon]="checkIcon" class="ds-color-picker-panel__preset-check"></fa-icon>
12339
+ }
12340
+ </button>
12341
+ }
12342
+ </div>
12343
+ </div>
12344
+ }
12345
+
12346
+ <!-- Couleurs récentes -->
12347
+ @if (showRecentColors && recentColors.length > 0) {
12348
+ <div class="ds-color-picker-panel__recent">
12349
+ <div class="ds-color-picker-panel__recent-label">Recent</div>
12350
+ <div class="ds-color-picker-panel__recent-grid">
12351
+ @for (color of recentColors; track color) {
12352
+ <button
12353
+ type="button"
12354
+ class="ds-color-picker-panel__preset"
12355
+ [style.background-color]="color"
12356
+ [class.ds-color-picker-panel__preset--selected]="color === value"
12357
+ (click)="selectPreset(color)"
12358
+ [attr.aria-label]="'Select ' + color">
12359
+ @if (color === value) {
12360
+ <fa-icon [icon]="checkIcon" class="ds-color-picker-panel__preset-check"></fa-icon>
12361
+ }
12362
+ </button>
12363
+ }
12364
+ </div>
12365
+ </div>
12366
+ }
12367
+ </div>
12368
+ `, isInline: true, styles: [".ds-color-picker-panel{position:relative;display:flex;flex-direction:column;gap:var(--space-3);width:300px;padding:var(--space-4);background:var(--colorpicker-panel-bg, var(--background-main));border:1px solid var(--colorpicker-panel-border, var(--border-default));border-radius:var(--colorpicker-panel-radius, var(--radius-2));box-shadow:var(--colorpicker-panel-shadow, var(--shadow-3))}.ds-color-picker-panel__spectrum{position:relative;width:100%;height:180px;border-radius:var(--radius-1);overflow:hidden;cursor:crosshair;border:1px solid var(--border-default)}.ds-color-picker-panel__spectrum-canvas{display:block;width:100%;height:100%}.ds-color-picker-panel__cursor{position:absolute;width:16px;height:16px;border:2px solid white;border-radius:50%;box-shadow:0 0 0 1px #0000004d,0 2px 4px #0003;transform:translate(-50%,-50%);pointer-events:none}.ds-color-picker-panel__hue-slider,.ds-color-picker-panel__alpha-slider{position:relative;width:100%;height:12px;border-radius:var(--radius-1);overflow:hidden}.ds-color-picker-panel__hue-slider{background:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.ds-color-picker-panel__alpha-slider .ds-color-picker-panel__alpha-bg{position:absolute;inset:0;background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.ds-color-picker-panel__slider{position:relative;width:100%;height:100%;-webkit-appearance:none;appearance:none;background:transparent;cursor:pointer;z-index:1}.ds-color-picker-panel__slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border:2px solid white;border-radius:50%;background:transparent;box-shadow:0 0 0 1px #0000004d,0 2px 4px #0003;cursor:pointer}.ds-color-picker-panel__slider::-moz-range-thumb{width:16px;height:16px;border:2px solid white;border-radius:50%;background:transparent;box-shadow:0 0 0 1px #0000004d,0 2px 4px #0003;cursor:pointer}.ds-color-picker-panel__slider:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.ds-color-picker-panel__slider--alpha{background:linear-gradient(to right,transparent 0%,currentColor 100%)}.ds-color-picker-panel__inputs{display:flex;gap:var(--space-2)}.ds-color-picker-panel__input-group{display:flex;flex-direction:column;gap:var(--space-1);flex:1}.ds-color-picker-panel__label{font-size:var(--font-size-1);font-weight:600;color:var(--text-muted);text-transform:uppercase}.ds-color-picker-panel__input{width:100%;padding:var(--space-1) var(--space-2);background:var(--colorpicker-input-bg, var(--background-main));border:1px solid var(--colorpicker-input-border, var(--border-default));border-radius:var(--radius-1);color:var(--colorpicker-input-text, var(--text-default));font-size:var(--font-size-2);font-family:monospace;text-align:center;outline:none;transition:border-color var(--duration-fast, .15s) ease}.ds-color-picker-panel__input:focus{border-color:var(--color-primary)}.ds-color-picker-panel__input:hover:not(:focus){border-color:var(--border-strong)}.ds-color-picker-panel__input[type=number]{-moz-appearance:textfield}.ds-color-picker-panel__input[type=number]::-webkit-outer-spin-button,.ds-color-picker-panel__input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.ds-color-picker-panel__presets,.ds-color-picker-panel__recent{display:flex;flex-direction:column;gap:var(--space-2)}.ds-color-picker-panel__presets-label,.ds-color-picker-panel__recent-label{font-size:var(--font-size-2);font-weight:600;color:var(--text-muted)}.ds-color-picker-panel__presets-grid,.ds-color-picker-panel__recent-grid{display:grid;grid-template-columns:repeat(8,1fr);gap:var(--space-2)}.ds-color-picker-panel__preset{position:relative;width:28px;height:28px;padding:0;background:currentColor;border:2px solid var(--colorpicker-preset-border, var(--border-default));border-radius:var(--radius-1);cursor:pointer;transition:transform var(--duration-fast, .15s) ease,border-color var(--duration-fast, .15s) ease;background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.ds-color-picker-panel__preset:after{content:\"\";position:absolute;inset:0;background-color:currentColor;border-radius:inherit}.ds-color-picker-panel__preset:hover:not(:disabled){transform:scale(1.1);border-color:var(--color-primary)}.ds-color-picker-panel__preset:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.ds-color-picker-panel__preset--selected{border-color:var(--color-primary);border-width:3px}.ds-color-picker-panel__preset-check{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;filter:drop-shadow(0 1px 2px rgba(0,0,0,.5));font-size:var(--font-size-2);z-index:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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 });
12369
+ }
12370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsColorPickerPanelComponent, decorators: [{
12371
+ type: Component,
12372
+ args: [{ selector: 'ds-color-picker-panel', standalone: true, imports: [CommonModule, FormsModule, FontAwesomeModule], template: `
12373
+ <div class="ds-color-picker-panel" role="dialog" aria-label="Color picker">
12374
+ <!-- Spectre de couleurs -->
12375
+ <div class="ds-color-picker-panel__spectrum">
12376
+ <canvas
12377
+ #spectrumCanvas
12378
+ class="ds-color-picker-panel__spectrum-canvas"
12379
+ width="280"
12380
+ height="180"
12381
+ (mousedown)="onSpectrumMouseDown($event)"
12382
+ (mousemove)="onSpectrumMouseMove($event)"
12383
+ (mouseup)="onSpectrumMouseUp()"
12384
+ (mouseleave)="onSpectrumMouseUp()">
12385
+ </canvas>
12386
+
12387
+ <!-- Curseur de sélection -->
12388
+ @if (cursorX() !== null && cursorY() !== null) {
12389
+ <div
12390
+ class="ds-color-picker-panel__cursor"
12391
+ [style.left.px]="cursorX()"
12392
+ [style.top.px]="cursorY()">
12393
+ </div>
12394
+ }
12395
+ </div>
12396
+
12397
+ <!-- Slider de teinte -->
12398
+ <div class="ds-color-picker-panel__hue-slider">
12399
+ <input
12400
+ type="range"
12401
+ class="ds-color-picker-panel__slider"
12402
+ min="0"
12403
+ max="360"
12404
+ step="1"
12405
+ [value]="hue()"
12406
+ (input)="onHueChange($event)"
12407
+ aria-label="Hue" />
12408
+ </div>
12409
+
12410
+ <!-- Slider alpha (optionnel) -->
12411
+ @if (showAlpha) {
12412
+ <div class="ds-color-picker-panel__alpha-slider">
12413
+ <div class="ds-color-picker-panel__alpha-bg"></div>
12414
+ <input
12415
+ type="range"
12416
+ class="ds-color-picker-panel__slider ds-color-picker-panel__slider--alpha"
12417
+ min="0"
12418
+ max="100"
12419
+ step="1"
12420
+ [value]="alpha() * 100"
12421
+ (input)="onAlphaChange($event)"
12422
+ aria-label="Alpha" />
12423
+ </div>
12424
+ }
12425
+
12426
+ <!-- Inputs manuels -->
12427
+ <div class="ds-color-picker-panel__inputs">
12428
+ @if (format === 'hex') {
12429
+ <div class="ds-color-picker-panel__input-group">
12430
+ <label class="ds-color-picker-panel__label">HEX</label>
12431
+ <input
12432
+ type="text"
12433
+ class="ds-color-picker-panel__input"
12434
+ [value]="hexValue()"
12435
+ (input)="onHexInput($event)"
12436
+ maxlength="7"
12437
+ placeholder="#000000" />
12438
+ </div>
12439
+ }
12440
+
12441
+ @if (format === 'rgb') {
12442
+ <div class="ds-color-picker-panel__input-group">
12443
+ <label class="ds-color-picker-panel__label">R</label>
12444
+ <input
12445
+ type="number"
12446
+ class="ds-color-picker-panel__input"
12447
+ min="0"
12448
+ max="255"
12449
+ [value]="rgb().r"
12450
+ (input)="onRGBInput('r', $event)" />
12451
+ </div>
12452
+ <div class="ds-color-picker-panel__input-group">
12453
+ <label class="ds-color-picker-panel__label">G</label>
12454
+ <input
12455
+ type="number"
12456
+ class="ds-color-picker-panel__input"
12457
+ min="0"
12458
+ max="255"
12459
+ [value]="rgb().g"
12460
+ (input)="onRGBInput('g', $event)" />
12461
+ </div>
12462
+ <div class="ds-color-picker-panel__input-group">
12463
+ <label class="ds-color-picker-panel__label">B</label>
12464
+ <input
12465
+ type="number"
12466
+ class="ds-color-picker-panel__input"
12467
+ min="0"
12468
+ max="255"
12469
+ [value]="rgb().b"
12470
+ (input)="onRGBInput('b', $event)" />
12471
+ </div>
12472
+ @if (showAlpha) {
12473
+ <div class="ds-color-picker-panel__input-group">
12474
+ <label class="ds-color-picker-panel__label">A</label>
12475
+ <input
12476
+ type="number"
12477
+ class="ds-color-picker-panel__input"
12478
+ min="0"
12479
+ max="1"
12480
+ step="0.01"
12481
+ [value]="alpha()"
12482
+ (input)="onAlphaInputManual($event)" />
12483
+ </div>
12484
+ }
12485
+ }
12486
+ </div>
12487
+
12488
+ <!-- Couleurs prédéfinies -->
12489
+ @if (presetColors.length > 0) {
12490
+ <div class="ds-color-picker-panel__presets">
12491
+ <div class="ds-color-picker-panel__presets-label">Presets</div>
12492
+ <div class="ds-color-picker-panel__presets-grid">
12493
+ @for (color of presetColors; track color) {
12494
+ <button
12495
+ type="button"
12496
+ class="ds-color-picker-panel__preset"
12497
+ [style.background-color]="color"
12498
+ [class.ds-color-picker-panel__preset--selected]="color === value"
12499
+ (click)="selectPreset(color)"
12500
+ [attr.aria-label]="'Select ' + color">
12501
+ @if (color === value) {
12502
+ <fa-icon [icon]="checkIcon" class="ds-color-picker-panel__preset-check"></fa-icon>
12503
+ }
12504
+ </button>
12505
+ }
12506
+ </div>
12507
+ </div>
12508
+ }
12509
+
12510
+ <!-- Couleurs récentes -->
12511
+ @if (showRecentColors && recentColors.length > 0) {
12512
+ <div class="ds-color-picker-panel__recent">
12513
+ <div class="ds-color-picker-panel__recent-label">Recent</div>
12514
+ <div class="ds-color-picker-panel__recent-grid">
12515
+ @for (color of recentColors; track color) {
12516
+ <button
12517
+ type="button"
12518
+ class="ds-color-picker-panel__preset"
12519
+ [style.background-color]="color"
12520
+ [class.ds-color-picker-panel__preset--selected]="color === value"
12521
+ (click)="selectPreset(color)"
12522
+ [attr.aria-label]="'Select ' + color">
12523
+ @if (color === value) {
12524
+ <fa-icon [icon]="checkIcon" class="ds-color-picker-panel__preset-check"></fa-icon>
12525
+ }
12526
+ </button>
12527
+ }
12528
+ </div>
12529
+ </div>
12530
+ }
12531
+ </div>
12532
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".ds-color-picker-panel{position:relative;display:flex;flex-direction:column;gap:var(--space-3);width:300px;padding:var(--space-4);background:var(--colorpicker-panel-bg, var(--background-main));border:1px solid var(--colorpicker-panel-border, var(--border-default));border-radius:var(--colorpicker-panel-radius, var(--radius-2));box-shadow:var(--colorpicker-panel-shadow, var(--shadow-3))}.ds-color-picker-panel__spectrum{position:relative;width:100%;height:180px;border-radius:var(--radius-1);overflow:hidden;cursor:crosshair;border:1px solid var(--border-default)}.ds-color-picker-panel__spectrum-canvas{display:block;width:100%;height:100%}.ds-color-picker-panel__cursor{position:absolute;width:16px;height:16px;border:2px solid white;border-radius:50%;box-shadow:0 0 0 1px #0000004d,0 2px 4px #0003;transform:translate(-50%,-50%);pointer-events:none}.ds-color-picker-panel__hue-slider,.ds-color-picker-panel__alpha-slider{position:relative;width:100%;height:12px;border-radius:var(--radius-1);overflow:hidden}.ds-color-picker-panel__hue-slider{background:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.ds-color-picker-panel__alpha-slider .ds-color-picker-panel__alpha-bg{position:absolute;inset:0;background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.ds-color-picker-panel__slider{position:relative;width:100%;height:100%;-webkit-appearance:none;appearance:none;background:transparent;cursor:pointer;z-index:1}.ds-color-picker-panel__slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border:2px solid white;border-radius:50%;background:transparent;box-shadow:0 0 0 1px #0000004d,0 2px 4px #0003;cursor:pointer}.ds-color-picker-panel__slider::-moz-range-thumb{width:16px;height:16px;border:2px solid white;border-radius:50%;background:transparent;box-shadow:0 0 0 1px #0000004d,0 2px 4px #0003;cursor:pointer}.ds-color-picker-panel__slider:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.ds-color-picker-panel__slider--alpha{background:linear-gradient(to right,transparent 0%,currentColor 100%)}.ds-color-picker-panel__inputs{display:flex;gap:var(--space-2)}.ds-color-picker-panel__input-group{display:flex;flex-direction:column;gap:var(--space-1);flex:1}.ds-color-picker-panel__label{font-size:var(--font-size-1);font-weight:600;color:var(--text-muted);text-transform:uppercase}.ds-color-picker-panel__input{width:100%;padding:var(--space-1) var(--space-2);background:var(--colorpicker-input-bg, var(--background-main));border:1px solid var(--colorpicker-input-border, var(--border-default));border-radius:var(--radius-1);color:var(--colorpicker-input-text, var(--text-default));font-size:var(--font-size-2);font-family:monospace;text-align:center;outline:none;transition:border-color var(--duration-fast, .15s) ease}.ds-color-picker-panel__input:focus{border-color:var(--color-primary)}.ds-color-picker-panel__input:hover:not(:focus){border-color:var(--border-strong)}.ds-color-picker-panel__input[type=number]{-moz-appearance:textfield}.ds-color-picker-panel__input[type=number]::-webkit-outer-spin-button,.ds-color-picker-panel__input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.ds-color-picker-panel__presets,.ds-color-picker-panel__recent{display:flex;flex-direction:column;gap:var(--space-2)}.ds-color-picker-panel__presets-label,.ds-color-picker-panel__recent-label{font-size:var(--font-size-2);font-weight:600;color:var(--text-muted)}.ds-color-picker-panel__presets-grid,.ds-color-picker-panel__recent-grid{display:grid;grid-template-columns:repeat(8,1fr);gap:var(--space-2)}.ds-color-picker-panel__preset{position:relative;width:28px;height:28px;padding:0;background:currentColor;border:2px solid var(--colorpicker-preset-border, var(--border-default));border-radius:var(--radius-1);cursor:pointer;transition:transform var(--duration-fast, .15s) ease,border-color var(--duration-fast, .15s) ease;background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0}.ds-color-picker-panel__preset:after{content:\"\";position:absolute;inset:0;background-color:currentColor;border-radius:inherit}.ds-color-picker-panel__preset:hover:not(:disabled){transform:scale(1.1);border-color:var(--color-primary)}.ds-color-picker-panel__preset:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.ds-color-picker-panel__preset--selected{border-color:var(--color-primary);border-width:3px}.ds-color-picker-panel__preset-check{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;filter:drop-shadow(0 1px 2px rgba(0,0,0,.5));font-size:var(--font-size-2);z-index:1}\n"] }]
12533
+ }], ctorParameters: () => [], propDecorators: { spectrumCanvas: [{
12534
+ type: ViewChild,
12535
+ args: ['spectrumCanvas']
12536
+ }], colorSelected: [{ type: i0.Output, args: ["colorSelected"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
12537
+
12538
+ /**
12539
+ * DsColorPicker - Composant de sélection de couleur
12540
+ *
12541
+ * @description
12542
+ * Sélecteur de couleur avec palette prédéfinie, spectre de couleurs,
12543
+ * support RGB/HSL, alpha channel optionnel, et intégration formulaires.
12544
+ *
12545
+ * @example
12546
+ * ```html
12547
+ * <ds-color-picker
12548
+ * [value]="'#3b82f6'"
12549
+ * [showAlpha]="true"
12550
+ * (colorChange)="onColorChange($event)">
12551
+ * </ds-color-picker>
12552
+ * ```
12553
+ */
12554
+ class DsColorPicker {
12555
+ overlay;
12556
+ elementRef;
12557
+ // Inputs
12558
+ value = input('', ...(ngDevMode ? [{ debugName: "value" }] : []));
12559
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
12560
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
12561
+ showAlpha = input(false, ...(ngDevMode ? [{ debugName: "showAlpha" }] : []));
12562
+ presetColors = input([], ...(ngDevMode ? [{ debugName: "presetColors" }] : []));
12563
+ format = input('hex', ...(ngDevMode ? [{ debugName: "format" }] : []));
12564
+ placeholder = input('Select color', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
12565
+ allowClear = input(true, ...(ngDevMode ? [{ debugName: "allowClear" }] : []));
12566
+ showRecentColors = input(true, ...(ngDevMode ? [{ debugName: "showRecentColors" }] : []));
12567
+ maxRecentColors = input(8, ...(ngDevMode ? [{ debugName: "maxRecentColors" }] : []));
12568
+ // Outputs
12569
+ colorChange = output();
12570
+ // Icons
12571
+ pickerIcon = faEyeDropper;
12572
+ clearIcon = faTimes;
12573
+ checkIcon = faCheck;
12574
+ // State
12575
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
12576
+ internalValue = signal('', ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
12577
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : []));
12578
+ recentColors = signal([], ...(ngDevMode ? [{ debugName: "recentColors" }] : []));
12579
+ // Overlay
12580
+ overlayRef = null;
12581
+ // Computed
12582
+ containerClasses = computed(() => {
12583
+ const classes = ['ds-color-picker'];
12584
+ classes.push(`ds-color-picker--${this.size()}`);
12585
+ if (this.disabled())
12586
+ classes.push('ds-color-picker--disabled');
12587
+ if (this.isFocused())
12588
+ classes.push('ds-color-picker--focused');
12589
+ if (this.isOpen())
12590
+ classes.push('ds-color-picker--open');
12591
+ return classes.join(' ');
12592
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
12593
+ displayValue = computed(() => {
12594
+ const val = this.internalValue();
12595
+ if (!val)
12596
+ return '';
12597
+ // Convertir selon le format demandé
12598
+ const format = this.format();
12599
+ if (format === 'hex') {
12600
+ return this.toHex(val);
12601
+ }
12602
+ else if (format === 'rgb') {
12603
+ return this.toRGB(val);
12604
+ }
12605
+ else if (format === 'hsl') {
12606
+ return this.toHSL(val);
12607
+ }
12608
+ return val;
12609
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
12610
+ previewColor = computed(() => {
12611
+ return this.internalValue() || 'transparent';
12612
+ }, ...(ngDevMode ? [{ debugName: "previewColor" }] : []));
12613
+ effectivePresets = computed(() => {
12614
+ const custom = this.presetColors();
12615
+ if (custom.length > 0)
12616
+ return custom;
12617
+ // Palette par défaut
12618
+ return [
12619
+ '#000000', '#ffffff', '#f87171', '#fb923c', '#fbbf24', '#facc15',
12620
+ '#a3e635', '#4ade80', '#34d399', '#2dd4bf', '#22d3ee', '#38bdf8',
12621
+ '#60a5fa', '#818cf8', '#a78bfa', '#c084fc', '#e879f9', '#f472b6',
12622
+ '#fb7185', '#f43f5e', '#ef4444', '#dc2626', '#b91c1c', '#991b1b',
12623
+ ];
12624
+ }, ...(ngDevMode ? [{ debugName: "effectivePresets" }] : []));
12625
+ // ControlValueAccessor
12626
+ onChange = () => { };
12627
+ onTouched = () => { };
12628
+ constructor(overlay, elementRef) {
12629
+ this.overlay = overlay;
12630
+ this.elementRef = elementRef;
12631
+ // Sync external value with internal
12632
+ effect(() => {
12633
+ const val = this.value();
12634
+ if (val !== this.internalValue()) {
12635
+ this.internalValue.set(val);
12636
+ }
12637
+ });
12638
+ // Charger les couleurs récentes depuis localStorage
12639
+ this.loadRecentColors();
12640
+ }
12641
+ writeValue(value) {
12642
+ this.internalValue.set(value || '');
12643
+ }
12644
+ registerOnChange(fn) {
12645
+ this.onChange = fn;
12646
+ }
12647
+ registerOnTouched(fn) {
12648
+ this.onTouched = fn;
12649
+ }
12650
+ setDisabledState(isDisabled) {
12651
+ // Géré par input disabled
12652
+ }
12653
+ toggle() {
12654
+ if (this.disabled())
12655
+ return;
12656
+ if (this.isOpen()) {
12657
+ this.close();
12658
+ }
12659
+ else {
12660
+ this.open();
12661
+ }
12662
+ }
12663
+ open() {
12664
+ if (this.disabled() || this.isOpen())
12665
+ return;
12666
+ this.isOpen.set(true);
12667
+ this.createOverlay();
12668
+ }
12669
+ close() {
12670
+ if (!this.isOpen())
12671
+ return;
12672
+ this.isOpen.set(false);
12673
+ this.destroyOverlay();
12674
+ this.onTouched();
12675
+ }
12676
+ onFocus() {
12677
+ if (!this.disabled()) {
12678
+ this.isFocused.set(true);
12679
+ }
12680
+ }
12681
+ onBlur() {
12682
+ this.isFocused.set(false);
12683
+ if (!this.isOpen()) {
12684
+ this.onTouched();
12685
+ }
12686
+ }
12687
+ clear() {
12688
+ if (this.disabled())
12689
+ return;
12690
+ this.updateValue('');
12691
+ }
12692
+ onColorSelected(color) {
12693
+ this.updateValue(color);
12694
+ this.addToRecentColors(color);
12695
+ }
12696
+ updateValue(value) {
12697
+ this.internalValue.set(value);
12698
+ this.onChange(value);
12699
+ this.colorChange.emit(value);
12700
+ }
12701
+ createOverlay() {
12702
+ if (this.overlayRef)
12703
+ return;
12704
+ const positionStrategy = this.overlay
12705
+ .position()
12706
+ .flexibleConnectedTo(this.elementRef.nativeElement.querySelector('.ds-color-picker__trigger'))
12707
+ .withPositions([
12708
+ {
12709
+ originX: 'start',
12710
+ originY: 'bottom',
12711
+ overlayX: 'start',
12712
+ overlayY: 'top',
12713
+ offsetY: 8,
12714
+ },
12715
+ {
12716
+ originX: 'start',
12717
+ originY: 'top',
12718
+ overlayX: 'start',
12719
+ overlayY: 'bottom',
12720
+ offsetY: -8,
12721
+ },
12722
+ ]);
12723
+ this.overlayRef = this.overlay.create({
12724
+ positionStrategy,
12725
+ hasBackdrop: true,
12726
+ backdropClass: 'cdk-overlay-transparent-backdrop',
12727
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
12728
+ });
12729
+ const portal = new ComponentPortal(DsColorPickerPanelComponent);
12730
+ const componentRef = this.overlayRef.attach(portal);
12731
+ // Configuration du panel
12732
+ componentRef.instance.value = this.internalValue();
12733
+ componentRef.instance.showAlpha = this.showAlpha();
12734
+ componentRef.instance.presetColors = this.effectivePresets();
12735
+ componentRef.instance.recentColors = this.recentColors();
12736
+ componentRef.instance.showRecentColors = this.showRecentColors();
12737
+ componentRef.instance.format = this.format();
12738
+ // Écouter les événements
12739
+ componentRef.instance.colorSelected.subscribe((color) => {
12740
+ this.onColorSelected(color);
12741
+ });
12742
+ componentRef.instance.closed.subscribe(() => {
12743
+ this.close();
12744
+ });
12745
+ // Fermer au clic sur backdrop
12746
+ this.overlayRef.backdropClick().subscribe(() => {
12747
+ this.close();
12748
+ });
12749
+ }
12750
+ destroyOverlay() {
12751
+ if (this.overlayRef) {
12752
+ this.overlayRef.dispose();
12753
+ this.overlayRef = null;
12754
+ }
12755
+ }
12756
+ addToRecentColors(color) {
12757
+ if (!this.showRecentColors() || !color)
12758
+ return;
12759
+ const recent = this.recentColors();
12760
+ const filtered = recent.filter(c => c !== color);
12761
+ const updated = [color, ...filtered].slice(0, this.maxRecentColors());
12762
+ this.recentColors.set(updated);
12763
+ this.saveRecentColors(updated);
12764
+ }
12765
+ loadRecentColors() {
12766
+ try {
12767
+ const stored = localStorage.getItem('ds-color-picker-recent');
12768
+ if (stored) {
12769
+ const colors = JSON.parse(stored);
12770
+ if (Array.isArray(colors)) {
12771
+ this.recentColors.set(colors.slice(0, this.maxRecentColors()));
12772
+ }
12773
+ }
12774
+ }
12775
+ catch (e) {
12776
+ // Ignorer les erreurs de parsing
12777
+ }
12778
+ }
12779
+ saveRecentColors(colors) {
12780
+ try {
12781
+ localStorage.setItem('ds-color-picker-recent', JSON.stringify(colors));
12782
+ }
12783
+ catch (e) {
12784
+ // Ignorer les erreurs de stockage
12785
+ }
12786
+ }
12787
+ // === Conversions de couleurs ===
12788
+ toHex(color) {
12789
+ if (color.startsWith('#'))
12790
+ return color;
12791
+ const rgb = this.parseRGB(color);
12792
+ if (rgb) {
12793
+ const r = rgb.r.toString(16).padStart(2, '0');
12794
+ const g = rgb.g.toString(16).padStart(2, '0');
12795
+ const b = rgb.b.toString(16).padStart(2, '0');
12796
+ return `#${r}${g}${b}`;
12797
+ }
12798
+ return color;
12799
+ }
12800
+ toRGB(color) {
12801
+ if (color.startsWith('rgb'))
12802
+ return color;
12803
+ const rgb = this.hexToRGB(color);
12804
+ if (rgb) {
12805
+ if (rgb.a !== undefined) {
12806
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})`;
12807
+ }
12808
+ return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
12809
+ }
12810
+ return color;
12811
+ }
12812
+ toHSL(color) {
12813
+ if (color.startsWith('hsl'))
12814
+ return color;
12815
+ const rgb = this.hexToRGB(color);
12816
+ if (rgb) {
12817
+ const hsl = this.rgbToHSL(rgb);
12818
+ if (hsl.a !== undefined) {
12819
+ return `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${hsl.a})`;
12820
+ }
12821
+ return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
12822
+ }
12823
+ return color;
12824
+ }
12825
+ hexToRGB(hex) {
12826
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(hex);
12827
+ if (!result)
12828
+ return null;
12829
+ return {
12830
+ r: parseInt(result[1], 16),
12831
+ g: parseInt(result[2], 16),
12832
+ b: parseInt(result[3], 16),
12833
+ a: result[4] ? parseInt(result[4], 16) / 255 : undefined,
12834
+ };
12835
+ }
12836
+ parseRGB(rgb) {
12837
+ const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
12838
+ if (!match)
12839
+ return null;
12840
+ return {
12841
+ r: parseInt(match[1], 10),
12842
+ g: parseInt(match[2], 10),
12843
+ b: parseInt(match[3], 10),
12844
+ a: match[4] ? parseFloat(match[4]) : undefined,
12845
+ };
12846
+ }
12847
+ rgbToHSL(rgb) {
12848
+ const r = rgb.r / 255;
12849
+ const g = rgb.g / 255;
12850
+ const b = rgb.b / 255;
12851
+ const max = Math.max(r, g, b);
12852
+ const min = Math.min(r, g, b);
12853
+ let h = 0;
12854
+ let s = 0;
12855
+ const l = (max + min) / 2;
12856
+ if (max !== min) {
12857
+ const d = max - min;
12858
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
12859
+ switch (max) {
12860
+ case r:
12861
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
12862
+ break;
12863
+ case g:
12864
+ h = ((b - r) / d + 2) / 6;
12865
+ break;
12866
+ case b:
12867
+ h = ((r - g) / d + 4) / 6;
12868
+ break;
12869
+ }
12870
+ }
12871
+ return {
12872
+ h: Math.round(h * 360),
12873
+ s: Math.round(s * 100),
12874
+ l: Math.round(l * 100),
12875
+ a: rgb.a,
12876
+ };
12877
+ }
12878
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsColorPicker, deps: [{ token: i1$3.Overlay }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
12879
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsColorPicker, isStandalone: true, selector: "ds-color-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showAlpha: { classPropertyName: "showAlpha", publicName: "showAlpha", isSignal: true, isRequired: false, transformFunction: null }, presetColors: { classPropertyName: "presetColors", publicName: "presetColors", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, allowClear: { classPropertyName: "allowClear", publicName: "allowClear", isSignal: true, isRequired: false, transformFunction: null }, showRecentColors: { classPropertyName: "showRecentColors", publicName: "showRecentColors", isSignal: true, isRequired: false, transformFunction: null }, maxRecentColors: { classPropertyName: "maxRecentColors", publicName: "maxRecentColors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { colorChange: "colorChange" }, providers: [
12880
+ {
12881
+ provide: NG_VALUE_ACCESSOR,
12882
+ useExisting: forwardRef(() => DsColorPicker),
12883
+ multi: true,
12884
+ },
12885
+ ], ngImport: i0, template: "<div [class]=\"containerClasses()\">\n <div class=\"ds-color-picker__trigger\" (click)=\"toggle()\">\n <!-- Preview de la couleur -->\n <div\n class=\"ds-color-picker__preview\"\n [style.background-color]=\"previewColor()\"\n [attr.aria-label]=\"'Color: ' + displayValue()\">\n </div>\n\n <!-- Input texte -->\n <input\n type=\"text\"\n class=\"ds-color-picker__input\"\n [value]=\"displayValue()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n readonly\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n [attr.aria-label]=\"placeholder()\" />\n\n <!-- Actions -->\n <div class=\"ds-color-picker__actions\">\n @if (allowClear() && internalValue()) {\n <button\n type=\"button\"\n class=\"ds-color-picker__action ds-color-picker__action--clear\"\n [disabled]=\"disabled()\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"'Clear color'\">\n <fa-icon [icon]=\"clearIcon\" aria-hidden=\"true\"></fa-icon>\n </button>\n }\n\n <button\n type=\"button\"\n class=\"ds-color-picker__action ds-color-picker__action--picker\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'Open color picker'\">\n <fa-icon [icon]=\"pickerIcon\" aria-hidden=\"true\"></fa-icon>\n </button>\n </div>\n </div>\n</div>\n", styles: [".ds-color-picker{position:relative;display:inline-flex;width:100%}.ds-color-picker__trigger{position:relative;display:flex;align-items:center;gap:var(--colorpicker-gap, var(--space-2));width:100%;background:var(--colorpicker-bg, var(--background-main));border:1px solid var(--colorpicker-border, var(--border-default));border-radius:var(--colorpicker-radius, var(--radius-2));padding:var(--colorpicker-padding, var(--space-2));cursor:pointer;transition:border-color var(--duration-fast, .15s) ease,box-shadow var(--duration-fast, .15s) ease}.ds-color-picker__trigger:hover:not([disabled]){border-color:var(--colorpicker-border-hover, var(--border-strong))}.ds-color-picker__preview{flex-shrink:0;width:var(--colorpicker-preview-size, 28px);height:var(--colorpicker-preview-size, 28px);border:2px solid var(--colorpicker-preview-border, var(--border-default));border-radius:var(--radius-1);background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0;position:relative}.ds-color-picker__preview:after{content:\"\";position:absolute;inset:0;background-color:currentColor;border-radius:inherit}.ds-color-picker__input{flex:1;min-width:0;background:transparent;border:none;color:var(--colorpicker-text, var(--text-default));font-size:var(--colorpicker-font-size, var(--font-size-3));outline:none;cursor:pointer}.ds-color-picker__input::placeholder{color:var(--colorpicker-placeholder, var(--text-muted))}.ds-color-picker__input:disabled{cursor:not-allowed}.ds-color-picker__actions{display:flex;align-items:center;gap:var(--space-1)}.ds-color-picker__action{display:flex;align-items:center;justify-content:center;width:var(--colorpicker-action-size, 24px);height:var(--colorpicker-action-size, 24px);padding:0;background:transparent;border:none;border-radius:var(--radius-1);color:var(--colorpicker-icon, var(--text-muted));cursor:pointer;transition:background-color var(--duration-fast, .15s) ease,color var(--duration-fast, .15s) ease}.ds-color-picker__action:hover:not(:disabled){background:var(--colorpicker-action-hover-bg, var(--surface-hover));color:var(--text-default)}.ds-color-picker__action:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.ds-color-picker__action:disabled{opacity:.5;cursor:not-allowed}.ds-color-picker__action--clear{color:var(--colorpicker-clear-icon, var(--text-muted))}.ds-color-picker__action--clear:hover:not(:disabled){color:var(--error)}.ds-color-picker--sm .ds-color-picker__trigger{padding:var(--colorpicker-padding-sm, var(--space-1) var(--space-2));height:var(--colorpicker-height-sm, 32px)}.ds-color-picker--sm .ds-color-picker__preview{width:var(--colorpicker-preview-size-sm, 20px);height:var(--colorpicker-preview-size-sm, 20px)}.ds-color-picker--sm .ds-color-picker__input{font-size:var(--colorpicker-font-size-sm, var(--font-size-2))}.ds-color-picker--sm .ds-color-picker__action{width:var(--colorpicker-action-size-sm, 20px);height:var(--colorpicker-action-size-sm, 20px);font-size:var(--font-size-2)}.ds-color-picker--md .ds-color-picker__trigger{padding:var(--colorpicker-padding-md, var(--space-2));height:var(--colorpicker-height-md, 40px)}.ds-color-picker--md .ds-color-picker__preview{width:var(--colorpicker-preview-size-md, 28px);height:var(--colorpicker-preview-size-md, 28px)}.ds-color-picker--md .ds-color-picker__input{font-size:var(--colorpicker-font-size-md, var(--font-size-3))}.ds-color-picker--md .ds-color-picker__action{width:var(--colorpicker-action-size-md, 24px);height:var(--colorpicker-action-size-md, 24px);font-size:var(--font-size-3)}.ds-color-picker--lg .ds-color-picker__trigger{padding:var(--colorpicker-padding-lg, var(--space-3));height:var(--colorpicker-height-lg, 48px)}.ds-color-picker--lg .ds-color-picker__preview{width:var(--colorpicker-preview-size-lg, 36px);height:var(--colorpicker-preview-size-lg, 36px)}.ds-color-picker--lg .ds-color-picker__input{font-size:var(--colorpicker-font-size-lg, var(--font-size-4))}.ds-color-picker--lg .ds-color-picker__action{width:var(--colorpicker-action-size-lg, 28px);height:var(--colorpicker-action-size-lg, 28px);font-size:var(--font-size-4)}.ds-color-picker--focused .ds-color-picker__trigger{border-color:var(--color-primary);box-shadow:0 0 0 3px var(--colorpicker-focus-shadow, rgba(59, 130, 246, .25))}.ds-color-picker--open .ds-color-picker__trigger{border-color:var(--color-primary)}.ds-color-picker--disabled{opacity:.6;pointer-events:none;cursor:not-allowed}\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"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: OverlayModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12886
+ }
12887
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsColorPicker, decorators: [{
12888
+ type: Component,
12889
+ args: [{ selector: 'ds-color-picker', standalone: true, imports: [CommonModule, FontAwesomeModule, FormsModule, OverlayModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
12890
+ {
12891
+ provide: NG_VALUE_ACCESSOR,
12892
+ useExisting: forwardRef(() => DsColorPicker),
12893
+ multi: true,
12894
+ },
12895
+ ], template: "<div [class]=\"containerClasses()\">\n <div class=\"ds-color-picker__trigger\" (click)=\"toggle()\">\n <!-- Preview de la couleur -->\n <div\n class=\"ds-color-picker__preview\"\n [style.background-color]=\"previewColor()\"\n [attr.aria-label]=\"'Color: ' + displayValue()\">\n </div>\n\n <!-- Input texte -->\n <input\n type=\"text\"\n class=\"ds-color-picker__input\"\n [value]=\"displayValue()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n readonly\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n [attr.aria-label]=\"placeholder()\" />\n\n <!-- Actions -->\n <div class=\"ds-color-picker__actions\">\n @if (allowClear() && internalValue()) {\n <button\n type=\"button\"\n class=\"ds-color-picker__action ds-color-picker__action--clear\"\n [disabled]=\"disabled()\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"'Clear color'\">\n <fa-icon [icon]=\"clearIcon\" aria-hidden=\"true\"></fa-icon>\n </button>\n }\n\n <button\n type=\"button\"\n class=\"ds-color-picker__action ds-color-picker__action--picker\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'Open color picker'\">\n <fa-icon [icon]=\"pickerIcon\" aria-hidden=\"true\"></fa-icon>\n </button>\n </div>\n </div>\n</div>\n", styles: [".ds-color-picker{position:relative;display:inline-flex;width:100%}.ds-color-picker__trigger{position:relative;display:flex;align-items:center;gap:var(--colorpicker-gap, var(--space-2));width:100%;background:var(--colorpicker-bg, var(--background-main));border:1px solid var(--colorpicker-border, var(--border-default));border-radius:var(--colorpicker-radius, var(--radius-2));padding:var(--colorpicker-padding, var(--space-2));cursor:pointer;transition:border-color var(--duration-fast, .15s) ease,box-shadow var(--duration-fast, .15s) ease}.ds-color-picker__trigger:hover:not([disabled]){border-color:var(--colorpicker-border-hover, var(--border-strong))}.ds-color-picker__preview{flex-shrink:0;width:var(--colorpicker-preview-size, 28px);height:var(--colorpicker-preview-size, 28px);border:2px solid var(--colorpicker-preview-border, var(--border-default));border-radius:var(--radius-1);background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0;position:relative}.ds-color-picker__preview:after{content:\"\";position:absolute;inset:0;background-color:currentColor;border-radius:inherit}.ds-color-picker__input{flex:1;min-width:0;background:transparent;border:none;color:var(--colorpicker-text, var(--text-default));font-size:var(--colorpicker-font-size, var(--font-size-3));outline:none;cursor:pointer}.ds-color-picker__input::placeholder{color:var(--colorpicker-placeholder, var(--text-muted))}.ds-color-picker__input:disabled{cursor:not-allowed}.ds-color-picker__actions{display:flex;align-items:center;gap:var(--space-1)}.ds-color-picker__action{display:flex;align-items:center;justify-content:center;width:var(--colorpicker-action-size, 24px);height:var(--colorpicker-action-size, 24px);padding:0;background:transparent;border:none;border-radius:var(--radius-1);color:var(--colorpicker-icon, var(--text-muted));cursor:pointer;transition:background-color var(--duration-fast, .15s) ease,color var(--duration-fast, .15s) ease}.ds-color-picker__action:hover:not(:disabled){background:var(--colorpicker-action-hover-bg, var(--surface-hover));color:var(--text-default)}.ds-color-picker__action:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.ds-color-picker__action:disabled{opacity:.5;cursor:not-allowed}.ds-color-picker__action--clear{color:var(--colorpicker-clear-icon, var(--text-muted))}.ds-color-picker__action--clear:hover:not(:disabled){color:var(--error)}.ds-color-picker--sm .ds-color-picker__trigger{padding:var(--colorpicker-padding-sm, var(--space-1) var(--space-2));height:var(--colorpicker-height-sm, 32px)}.ds-color-picker--sm .ds-color-picker__preview{width:var(--colorpicker-preview-size-sm, 20px);height:var(--colorpicker-preview-size-sm, 20px)}.ds-color-picker--sm .ds-color-picker__input{font-size:var(--colorpicker-font-size-sm, var(--font-size-2))}.ds-color-picker--sm .ds-color-picker__action{width:var(--colorpicker-action-size-sm, 20px);height:var(--colorpicker-action-size-sm, 20px);font-size:var(--font-size-2)}.ds-color-picker--md .ds-color-picker__trigger{padding:var(--colorpicker-padding-md, var(--space-2));height:var(--colorpicker-height-md, 40px)}.ds-color-picker--md .ds-color-picker__preview{width:var(--colorpicker-preview-size-md, 28px);height:var(--colorpicker-preview-size-md, 28px)}.ds-color-picker--md .ds-color-picker__input{font-size:var(--colorpicker-font-size-md, var(--font-size-3))}.ds-color-picker--md .ds-color-picker__action{width:var(--colorpicker-action-size-md, 24px);height:var(--colorpicker-action-size-md, 24px);font-size:var(--font-size-3)}.ds-color-picker--lg .ds-color-picker__trigger{padding:var(--colorpicker-padding-lg, var(--space-3));height:var(--colorpicker-height-lg, 48px)}.ds-color-picker--lg .ds-color-picker__preview{width:var(--colorpicker-preview-size-lg, 36px);height:var(--colorpicker-preview-size-lg, 36px)}.ds-color-picker--lg .ds-color-picker__input{font-size:var(--colorpicker-font-size-lg, var(--font-size-4))}.ds-color-picker--lg .ds-color-picker__action{width:var(--colorpicker-action-size-lg, 28px);height:var(--colorpicker-action-size-lg, 28px);font-size:var(--font-size-4)}.ds-color-picker--focused .ds-color-picker__trigger{border-color:var(--color-primary);box-shadow:0 0 0 3px var(--colorpicker-focus-shadow, rgba(59, 130, 246, .25))}.ds-color-picker--open .ds-color-picker__trigger{border-color:var(--color-primary)}.ds-color-picker--disabled{opacity:.6;pointer-events:none;cursor:not-allowed}\n"] }]
12896
+ }], ctorParameters: () => [{ type: i1$3.Overlay }, { type: i0.ElementRef }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showAlpha: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAlpha", required: false }] }], presetColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "presetColors", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], allowClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowClear", required: false }] }], showRecentColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRecentColors", required: false }] }], maxRecentColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRecentColors", required: false }] }], colorChange: [{ type: i0.Output, args: ["colorChange"] }] } });
12897
+
10166
12898
  /*
10167
12899
  * Components barrel export
10168
12900
  */
@@ -10734,5 +13466,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
10734
13466
  * Generated bundle index. Do not edit.
10735
13467
  */
10736
13468
 
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 };
13469
+ 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, DsColorPicker, DsCombobox, DsContainer, DsDatePicker, DsDivider, DsDrawer, DsDropdown, DsEmpty, DsFileUpload, DsI18nService, DsInputField, DsInputNumber, DsInputTextarea, DsMenu, DsModalComponent, DsNotificationContainerComponent, DsNotificationItemComponent, DsNotificationService, DsPagination, DsPasswordStrength, DsPopover, DsPopoverComponent, DsProgressBar, DsRadioGroup, DsRating, DsSearchInput, DsSegmentedControl, 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
13470
  //# sourceMappingURL=kksdev-ds-angular.mjs.map