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