@kksdev/ds-angular 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7225,6 +7225,313 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
7225
7225
  ], styles: [".ds-date-picker{display:inline-flex;flex-direction:column;background:var(--datepicker-bg, var(--background-main, #fff));border:1px solid var(--datepicker-border, var(--border-default, #d1d5db));border-radius:var(--datepicker-radius, var(--radius-2, 6px));box-shadow:var(--datepicker-shadow, var(--shadow-md, 0 4px 6px -1px rgba(0, 0, 0, .1)));overflow:hidden;-webkit-user-select:none;user-select:none}.ds-date-picker--sm{width:260px;font-size:var(--font-size-2, .875rem)}.ds-date-picker--sm .ds-date-picker__header{padding:var(--space-2, .5rem)}.ds-date-picker--sm .ds-date-picker__nav{width:24px;height:24px}.ds-date-picker--sm .ds-date-picker__day{width:28px;height:28px}.ds-date-picker--sm .ds-date-picker__weekday{width:28px;height:24px}.ds-date-picker--md{width:300px;font-size:var(--font-size-3, 1rem)}.ds-date-picker--md .ds-date-picker__header{padding:var(--space-3, .75rem)}.ds-date-picker--md .ds-date-picker__nav{width:32px;height:32px}.ds-date-picker--md .ds-date-picker__day{width:36px;height:36px}.ds-date-picker--md .ds-date-picker__weekday{width:36px;height:28px}.ds-date-picker--lg{width:360px;font-size:var(--font-size-4, 1.125rem)}.ds-date-picker--lg .ds-date-picker__header{padding:var(--space-4, 1rem)}.ds-date-picker--lg .ds-date-picker__nav{width:40px;height:40px}.ds-date-picker--lg .ds-date-picker__day{width:44px;height:44px}.ds-date-picker--lg .ds-date-picker__weekday{width:44px;height:32px}.ds-date-picker--disabled{opacity:.6;pointer-events:none}.ds-date-picker__header{display:flex;align-items:center;justify-content:space-between;background:var(--datepicker-header-bg, var(--background-subtle, #f9fafb));border-bottom:1px solid var(--datepicker-border, var(--border-default, #d1d5db))}.ds-date-picker__title{display:flex;align-items:center;gap:var(--space-1, .25rem)}.ds-date-picker__month-btn,.ds-date-picker__year-btn{padding:var(--space-1, .25rem) var(--space-2, .5rem);background:transparent;border:none;border-radius:var(--radius-1, 4px);color:var(--text-default, #1f2937);font-weight:600;cursor:pointer;transition:background-color .15s ease}.ds-date-picker__month-btn:hover:not(:disabled),.ds-date-picker__year-btn:hover:not(:disabled){background:var(--background-muted, #e5e7eb)}.ds-date-picker__month-btn:focus-visible,.ds-date-picker__year-btn:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:1px}.ds-date-picker__month-btn:disabled,.ds-date-picker__year-btn:disabled{cursor:not-allowed;opacity:.5}.ds-date-picker__nav{display:flex;align-items:center;justify-content:center;background:transparent;border:none;border-radius:var(--radius-1, 4px);color:var(--text-muted, #6b7280);cursor:pointer;transition:background-color .15s ease,color .15s ease}.ds-date-picker__nav:hover:not(:disabled){background:var(--background-muted, #e5e7eb);color:var(--text-default, #1f2937)}.ds-date-picker__nav:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:1px}.ds-date-picker__nav:disabled{cursor:not-allowed;opacity:.5}.ds-date-picker__calendar{padding:var(--space-2, .5rem)}.ds-date-picker__weekdays{display:flex;margin-bottom:var(--space-1, .25rem)}.ds-date-picker__weekday{display:flex;align-items:center;justify-content:center;color:var(--text-muted, #6b7280);font-size:.75em;font-weight:500;text-transform:uppercase}.ds-date-picker__days{display:flex;flex-direction:column;gap:var(--space-1, .25rem)}.ds-date-picker__week{display:flex}.ds-date-picker__day{display:flex;align-items:center;justify-content:center;background:transparent;border:none;border-radius:var(--radius-1, 4px);color:var(--text-default, #1f2937);font-weight:400;cursor:pointer;transition:background-color .15s ease,color .15s ease}.ds-date-picker__day:hover:not(:disabled):not(.ds-date-picker__day--selected){background:var(--background-subtle, #f3f4f6)}.ds-date-picker__day:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:-2px;z-index:1}.ds-date-picker__day--other-month{color:var(--text-muted, #9ca3af)}.ds-date-picker__day--today{font-weight:600;border:1px solid var(--color-primary, #3b82f6)}.ds-date-picker__day--selected{background:var(--color-primary, #3b82f6);color:var(--color-primary-text, #fff);font-weight:600}.ds-date-picker__day--selected:hover:not(:disabled){background:var(--color-primary-hover, #2563eb)}.ds-date-picker__day--range-start{background:var(--color-primary, #3b82f6);color:var(--color-primary-text, #fff);border-radius:var(--radius-1, 4px) 0 0 var(--radius-1, 4px)}.ds-date-picker__day--range-end{background:var(--color-primary, #3b82f6);color:var(--color-primary-text, #fff);border-radius:0 var(--radius-1, 4px) var(--radius-1, 4px) 0}.ds-date-picker__day--in-range{background:var(--color-primary-subtle, rgba(59, 130, 246, .15));border-radius:0}.ds-date-picker__day--disabled{opacity:.4;cursor:not-allowed}.ds-date-picker__month-picker,.ds-date-picker__year-picker{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-2, .5rem);padding:var(--space-3, .75rem);max-height:240px;overflow-y:auto}.ds-date-picker__month-option,.ds-date-picker__year-option{padding:var(--space-2, .5rem);background:transparent;border:1px solid transparent;border-radius:var(--radius-1, 4px);color:var(--text-default, #1f2937);cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.ds-date-picker__month-option:hover:not(:disabled),.ds-date-picker__year-option:hover:not(:disabled){background:var(--background-subtle, #f3f4f6);border-color:var(--border-default, #d1d5db)}.ds-date-picker__month-option:focus-visible,.ds-date-picker__year-option:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:1px}.ds-date-picker__month-option--selected,.ds-date-picker__year-option--selected{background:var(--color-primary, #3b82f6);color:var(--color-primary-text, #fff);border-color:var(--color-primary, #3b82f6)}.ds-date-picker__month-option--selected:hover:not(:disabled),.ds-date-picker__year-option--selected:hover:not(:disabled){background:var(--color-primary-hover, #2563eb);border-color:var(--color-primary-hover, #2563eb)}.ds-date-picker__month-option:disabled,.ds-date-picker__year-option:disabled{cursor:not-allowed;opacity:.5}.ds-date-picker__actions{display:flex;justify-content:center;gap:var(--space-2, .5rem);padding:var(--space-2, .5rem);border-top:1px solid var(--datepicker-border, var(--border-default, #d1d5db));background:var(--datepicker-footer-bg, var(--background-subtle, #f9fafb))}.ds-date-picker__action{padding:var(--space-1, .25rem) var(--space-3, .75rem);background:transparent;border:1px solid var(--border-default, #d1d5db);border-radius:var(--radius-1, 4px);color:var(--text-default, #1f2937);font-size:.875em;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.ds-date-picker__action:hover:not(:disabled){background:var(--background-muted, #e5e7eb);border-color:var(--border-hover, #9ca3af)}.ds-date-picker__action:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:1px}.ds-date-picker__action:disabled{cursor:not-allowed;opacity:.5}\n"] }]
7226
7226
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], showTodayButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTodayButton", required: false }] }], showClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClearButton", required: false }] }], todayLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "todayLabel", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], prevMonthLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevMonthLabel", required: false }] }], nextMonthLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextMonthLabel", required: false }] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }], rangeChange: [{ type: i0.Output, args: ["rangeChange"] }] } });
7227
7227
 
7228
+ class DsAvatar {
7229
+ /** URL de l'image avatar */
7230
+ src = input(undefined, ...(ngDevMode ? [{ debugName: "src" }] : []));
7231
+ /** Texte alternatif pour l'image */
7232
+ alt = input('', ...(ngDevMode ? [{ debugName: "alt" }] : []));
7233
+ /** Nom complet pour générer les initiales */
7234
+ name = input('', ...(ngDevMode ? [{ debugName: "name" }] : []));
7235
+ /** Initiales personnalisées (priorité sur name) */
7236
+ initials = input(undefined, ...(ngDevMode ? [{ debugName: "initials" }] : []));
7237
+ /** Forme de l'avatar */
7238
+ shape = input('circle', ...(ngDevMode ? [{ debugName: "shape" }] : []));
7239
+ /** Taille de l'avatar */
7240
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
7241
+ /** Générer automatiquement une couleur de fond à partir des initiales */
7242
+ autoColor = input(false, ...(ngDevMode ? [{ debugName: "autoColor" }] : []));
7243
+ /** État interne pour l'erreur de chargement d'image */
7244
+ imageError = signal(false, ...(ngDevMode ? [{ debugName: "imageError" }] : []));
7245
+ /** Indique si l'image doit être affichée */
7246
+ showImage = computed(() => !!this.src() && !this.imageError(), ...(ngDevMode ? [{ debugName: "showImage" }] : []));
7247
+ /** Initiales à afficher (générées ou fournies) */
7248
+ displayInitials = computed(() => {
7249
+ const customInitials = this.initials();
7250
+ if (customInitials) {
7251
+ return customInitials.substring(0, 2).toUpperCase();
7252
+ }
7253
+ const name = this.name();
7254
+ if (!name)
7255
+ return '';
7256
+ const parts = name.trim().split(/\s+/);
7257
+ if (parts.length === 1) {
7258
+ return parts[0].substring(0, 2).toUpperCase();
7259
+ }
7260
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
7261
+ }, ...(ngDevMode ? [{ debugName: "displayInitials" }] : []));
7262
+ /** Classes CSS du conteneur */
7263
+ containerClasses = computed(() => ({
7264
+ 'ds-avatar': true,
7265
+ [`ds-avatar--${this.shape()}`]: true,
7266
+ [`ds-avatar--${this.size()}`]: true,
7267
+ 'ds-avatar--has-image': this.showImage(),
7268
+ 'ds-avatar--initials': !this.showImage(),
7269
+ }), ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
7270
+ /** Couleur de fond auto-générée */
7271
+ generatedBgColor = computed(() => {
7272
+ if (!this.autoColor())
7273
+ return null;
7274
+ const initials = this.displayInitials();
7275
+ if (!initials)
7276
+ return null;
7277
+ // Algorithme de hash simple vers couleur HSL
7278
+ let hash = 0;
7279
+ for (let i = 0; i < initials.length; i++) {
7280
+ hash = initials.charCodeAt(i) + ((hash << 5) - hash);
7281
+ }
7282
+ const hue = Math.abs(hash % 360);
7283
+ return `hsl(${hue}, 65%, 45%)`;
7284
+ }, ...(ngDevMode ? [{ debugName: "generatedBgColor" }] : []));
7285
+ /** Label ARIA pour l'accessibilité */
7286
+ ariaLabel = computed(() => {
7287
+ const alt = this.alt();
7288
+ if (alt)
7289
+ return alt;
7290
+ const name = this.name();
7291
+ if (name)
7292
+ return `Avatar de ${name}`;
7293
+ return 'Avatar';
7294
+ }, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
7295
+ /** Gestionnaire d'erreur de chargement d'image */
7296
+ onImageError() {
7297
+ this.imageError.set(true);
7298
+ }
7299
+ /** Gestionnaire de chargement réussi d'image */
7300
+ onImageLoad() {
7301
+ this.imageError.set(false);
7302
+ }
7303
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
7304
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsAvatar, isStandalone: true, selector: "ds-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, autoColor: { classPropertyName: "autoColor", publicName: "autoColor", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n [ngClass]=\"containerClasses()\"\n [style.background-color]=\"generatedBgColor()\"\n [attr.role]=\"showImage() ? null : 'img'\"\n [attr.aria-label]=\"showImage() ? null : ariaLabel()\"\n>\n @if (showImage()) {\n <img\n [src]=\"src()\"\n [alt]=\"ariaLabel()\"\n class=\"ds-avatar__image\"\n (error)=\"onImageError()\"\n (load)=\"onImageLoad()\"\n />\n } @else {\n <span class=\"ds-avatar__initials\" aria-hidden=\"true\">\n {{ displayInitials() }}\n </span>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:inline-block}.ds-avatar{display:flex;align-items:center;justify-content:center;overflow:hidden;background-color:var(--avatar-bg);color:var(--avatar-text);font-family:var(--font-family-base);font-weight:var(--font-weight-medium);-webkit-user-select:none;user-select:none}.ds-avatar--sm{width:var(--avatar-size-sm);height:var(--avatar-size-sm);font-size:var(--avatar-font-size-sm)}.ds-avatar--md{width:var(--avatar-size-md);height:var(--avatar-size-md);font-size:var(--avatar-font-size-md)}.ds-avatar--lg{width:var(--avatar-size-lg);height:var(--avatar-size-lg);font-size:var(--avatar-font-size-lg)}.ds-avatar--xl{width:var(--avatar-size-xl);height:var(--avatar-size-xl);font-size:var(--avatar-font-size-xl)}.ds-avatar--circle{border-radius:var(--avatar-radius-circle)}.ds-avatar--rounded{border-radius:var(--avatar-radius-rounded)}.ds-avatar--square{border-radius:var(--avatar-radius-square)}.ds-avatar--initials{background-color:var(--avatar-placeholder-bg);color:var(--avatar-placeholder-text)}.ds-avatar--has-image{background-color:transparent}.ds-avatar__image{width:100%;height:100%;object-fit:cover}.ds-avatar__initials{line-height:1;text-transform:uppercase;letter-spacing:var(--letter-spacing-tight)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
7305
+ }
7306
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsAvatar, decorators: [{
7307
+ type: Component,
7308
+ args: [{ selector: 'ds-avatar', standalone: true, imports: [CommonModule], template: "<div\n [ngClass]=\"containerClasses()\"\n [style.background-color]=\"generatedBgColor()\"\n [attr.role]=\"showImage() ? null : 'img'\"\n [attr.aria-label]=\"showImage() ? null : ariaLabel()\"\n>\n @if (showImage()) {\n <img\n [src]=\"src()\"\n [alt]=\"ariaLabel()\"\n class=\"ds-avatar__image\"\n (error)=\"onImageError()\"\n (load)=\"onImageLoad()\"\n />\n } @else {\n <span class=\"ds-avatar__initials\" aria-hidden=\"true\">\n {{ displayInitials() }}\n </span>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:inline-block}.ds-avatar{display:flex;align-items:center;justify-content:center;overflow:hidden;background-color:var(--avatar-bg);color:var(--avatar-text);font-family:var(--font-family-base);font-weight:var(--font-weight-medium);-webkit-user-select:none;user-select:none}.ds-avatar--sm{width:var(--avatar-size-sm);height:var(--avatar-size-sm);font-size:var(--avatar-font-size-sm)}.ds-avatar--md{width:var(--avatar-size-md);height:var(--avatar-size-md);font-size:var(--avatar-font-size-md)}.ds-avatar--lg{width:var(--avatar-size-lg);height:var(--avatar-size-lg);font-size:var(--avatar-font-size-lg)}.ds-avatar--xl{width:var(--avatar-size-xl);height:var(--avatar-size-xl);font-size:var(--avatar-font-size-xl)}.ds-avatar--circle{border-radius:var(--avatar-radius-circle)}.ds-avatar--rounded{border-radius:var(--avatar-radius-rounded)}.ds-avatar--square{border-radius:var(--avatar-radius-square)}.ds-avatar--initials{background-color:var(--avatar-placeholder-bg);color:var(--avatar-placeholder-text)}.ds-avatar--has-image{background-color:transparent}.ds-avatar__image{width:100%;height:100%;object-fit:cover}.ds-avatar__initials{line-height:1;text-transform:uppercase;letter-spacing:var(--letter-spacing-tight)}\n"] }]
7309
+ }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], autoColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoColor", required: false }] }] } });
7310
+
7311
+ class DsMenu {
7312
+ /** Liste des items du menu */
7313
+ items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
7314
+ /** Taille du menu */
7315
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
7316
+ /** Événement déclencheur */
7317
+ trigger = input('click', ...(ngDevMode ? [{ debugName: "trigger" }] : []));
7318
+ /** Fermer le menu après sélection */
7319
+ closeOnSelect = input(true, ...(ngDevMode ? [{ debugName: "closeOnSelect" }] : []));
7320
+ /** Label ARIA pour l'accessibilité */
7321
+ ariaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
7322
+ /** Événement émis lors de la sélection d'un item */
7323
+ itemSelected = output();
7324
+ /** Événement émis à l'ouverture du menu */
7325
+ opened = output();
7326
+ /** Événement émis à la fermeture du menu */
7327
+ closed = output();
7328
+ triggerElement;
7329
+ menuItemsRef;
7330
+ /** État interne d'ouverture */
7331
+ menuOpen = signal(false, ...(ngDevMode ? [{ debugName: "menuOpen" }] : []));
7332
+ /** Index de l'item actif pour la navigation clavier */
7333
+ internalActiveIndex = signal(-1, ...(ngDevMode ? [{ debugName: "internalActiveIndex" }] : []));
7334
+ /** Exposition en lecture seule de l'index actif */
7335
+ activeIndex = computed(() => this.internalActiveIndex(), ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
7336
+ /** État d'ouverture du menu (lecture seule) */
7337
+ isOpen = computed(() => this.menuOpen(), ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
7338
+ /** Positions pour l'overlay CDK */
7339
+ overlayPositions = DROPDOWN_POSITIONS;
7340
+ /** Classes CSS du panel menu */
7341
+ menuClasses = computed(() => ({
7342
+ 'ds-menu__panel': true,
7343
+ [`ds-menu__panel--${this.size()}`]: true,
7344
+ }), ...(ngDevMode ? [{ debugName: "menuClasses" }] : []));
7345
+ /** ID de l'item actif pour aria-activedescendant */
7346
+ activeDescendantId = computed(() => {
7347
+ const items = this.items();
7348
+ const index = this.internalActiveIndex();
7349
+ if (index < 0 || index >= items.length)
7350
+ return undefined;
7351
+ return `menu-item-${items[index].id}`;
7352
+ }, ...(ngDevMode ? [{ debugName: "activeDescendantId" }] : []));
7353
+ /** Ouvre le menu */
7354
+ open() {
7355
+ if (this.isOpen())
7356
+ return;
7357
+ this.menuOpen.set(true);
7358
+ this.internalActiveIndex.set(this.findFirstEnabledIndex());
7359
+ this.opened.emit();
7360
+ queueMicrotask(() => this.focusActiveItem());
7361
+ }
7362
+ /** Ferme le menu */
7363
+ close(refocusTrigger = true) {
7364
+ if (!this.isOpen())
7365
+ return;
7366
+ this.menuOpen.set(false);
7367
+ this.internalActiveIndex.set(-1);
7368
+ if (refocusTrigger) {
7369
+ this.triggerElement?.nativeElement?.focus();
7370
+ }
7371
+ this.closed.emit();
7372
+ }
7373
+ /** Bascule l'état du menu */
7374
+ toggle() {
7375
+ this.isOpen() ? this.close() : this.open();
7376
+ }
7377
+ /** Sélectionne un item */
7378
+ selectItem(item) {
7379
+ if (item.disabled)
7380
+ return;
7381
+ this.itemSelected.emit(item);
7382
+ if (this.closeOnSelect()) {
7383
+ this.close();
7384
+ }
7385
+ }
7386
+ /** Gestionnaire de clic sur le trigger */
7387
+ onTriggerClick(event) {
7388
+ if (this.trigger() === 'click') {
7389
+ event.preventDefault();
7390
+ this.toggle();
7391
+ }
7392
+ }
7393
+ /** Gestionnaire de clic droit sur le trigger */
7394
+ onTriggerContextMenu(event) {
7395
+ if (this.trigger() === 'contextmenu') {
7396
+ event.preventDefault();
7397
+ this.open();
7398
+ }
7399
+ }
7400
+ /** Gestionnaire de clavier sur le trigger */
7401
+ onTriggerKeydown(event) {
7402
+ switch (event.key) {
7403
+ case 'ArrowDown':
7404
+ case 'Enter':
7405
+ case ' ':
7406
+ event.preventDefault();
7407
+ this.open();
7408
+ break;
7409
+ case 'Escape':
7410
+ if (this.isOpen()) {
7411
+ event.preventDefault();
7412
+ this.close();
7413
+ }
7414
+ break;
7415
+ }
7416
+ }
7417
+ /** Gestionnaire de clavier dans le menu */
7418
+ onMenuKeydown(event) {
7419
+ if (!this.isOpen())
7420
+ return;
7421
+ switch (event.key) {
7422
+ case 'ArrowDown':
7423
+ event.preventDefault();
7424
+ this.moveToNextEnabled(1);
7425
+ break;
7426
+ case 'ArrowUp':
7427
+ event.preventDefault();
7428
+ this.moveToNextEnabled(-1);
7429
+ break;
7430
+ case 'Home':
7431
+ event.preventDefault();
7432
+ this.setActiveIndex(this.findFirstEnabledIndex());
7433
+ break;
7434
+ case 'End':
7435
+ event.preventDefault();
7436
+ this.setActiveIndex(this.findLastEnabledIndex());
7437
+ break;
7438
+ case 'Enter':
7439
+ case ' ':
7440
+ event.preventDefault();
7441
+ this.selectActiveItem();
7442
+ break;
7443
+ case 'Escape':
7444
+ event.preventDefault();
7445
+ this.close();
7446
+ break;
7447
+ case 'Tab':
7448
+ this.close(false);
7449
+ break;
7450
+ }
7451
+ }
7452
+ /** Met à jour l'index actif au survol */
7453
+ onItemMouseEnter(index) {
7454
+ if (!this.isOpen())
7455
+ return;
7456
+ const item = this.items()[index];
7457
+ if (!item?.disabled) {
7458
+ this.internalActiveIndex.set(index);
7459
+ }
7460
+ }
7461
+ /** Gestionnaire de clic sur le backdrop */
7462
+ onBackdropClick() {
7463
+ this.close();
7464
+ }
7465
+ ngOnDestroy() {
7466
+ this.close(false);
7467
+ }
7468
+ /** Trouve l'index du premier item activable */
7469
+ findFirstEnabledIndex() {
7470
+ return this.items().findIndex(item => !item.disabled);
7471
+ }
7472
+ /** Trouve l'index du dernier item activable */
7473
+ findLastEnabledIndex() {
7474
+ const items = this.items();
7475
+ for (let i = items.length - 1; i >= 0; i--) {
7476
+ if (!items[i].disabled)
7477
+ return i;
7478
+ }
7479
+ return -1;
7480
+ }
7481
+ /** Navigue vers le prochain item activable */
7482
+ moveToNextEnabled(direction) {
7483
+ const items = this.items();
7484
+ if (!items.length)
7485
+ return;
7486
+ let index = this.internalActiveIndex();
7487
+ const startIndex = index;
7488
+ do {
7489
+ index += direction;
7490
+ if (index < 0)
7491
+ index = items.length - 1;
7492
+ if (index >= items.length)
7493
+ index = 0;
7494
+ if (!items[index].disabled) {
7495
+ this.setActiveIndex(index);
7496
+ return;
7497
+ }
7498
+ } while (index !== startIndex);
7499
+ }
7500
+ /** Définit l'index actif et focus l'élément */
7501
+ setActiveIndex(index) {
7502
+ this.internalActiveIndex.set(index);
7503
+ this.focusActiveItem();
7504
+ }
7505
+ /** Focus l'item actif */
7506
+ focusActiveItem() {
7507
+ const items = this.menuItemsRef?.toArray();
7508
+ const index = this.internalActiveIndex();
7509
+ if (items && index >= 0 && index < items.length) {
7510
+ items[index].nativeElement.focus();
7511
+ }
7512
+ }
7513
+ /** Sélectionne l'item actuellement actif */
7514
+ selectActiveItem() {
7515
+ const items = this.items();
7516
+ const index = this.internalActiveIndex();
7517
+ if (index >= 0 && index < items.length) {
7518
+ this.selectItem(items[index]);
7519
+ }
7520
+ }
7521
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
7522
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsMenu, isStandalone: true, selector: "ds-menu", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelected: "itemSelected", opened: "opened", closed: "closed" }, viewQueries: [{ propertyName: "triggerElement", first: true, predicate: ["menuTrigger"], descendants: true, read: ElementRef }, { propertyName: "menuItemsRef", predicate: ["menuItemRef"], descendants: true }], ngImport: i0, template: "<div\n #menuTrigger\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n (click)=\"onTriggerClick($event)\"\n (contextmenu)=\"onTriggerContextMenu($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"isOpen()\"\n class=\"ds-menu__trigger\"\n>\n <ng-content select=\"[dsMenuTrigger]\"></ng-content>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'ds-menu-backdrop'\"\n (backdropClick)=\"onBackdropClick()\"\n (detach)=\"close(false)\"\n>\n <div\n [ngClass]=\"menuClasses()\"\n role=\"menu\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n (keydown)=\"onMenuKeydown($event)\"\n >\n @for (item of items(); track item.id; let index = $index) {\n @if (item.dividerBefore) {\n <div class=\"ds-menu__divider\" role=\"separator\"></div>\n }\n <button\n #menuItemRef\n type=\"button\"\n class=\"ds-menu__item\"\n [class.ds-menu__item--disabled]=\"item.disabled\"\n [class.ds-menu__item--active]=\"activeIndex() === index\"\n [attr.id]=\"'menu-item-' + item.id\"\n [attr.aria-disabled]=\"item.disabled || null\"\n [disabled]=\"item.disabled\"\n role=\"menuitem\"\n (click)=\"selectItem(item)\"\n (mouseenter)=\"onItemMouseEnter(index)\"\n >\n @if (item.icon) {\n <fa-icon [icon]=\"item.icon\" class=\"ds-menu__item-icon\"></fa-icon>\n }\n <span class=\"ds-menu__item-label\">{{ item.label }}</span>\n </button>\n }\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:inline-block}.ds-menu__trigger{display:inline-block;cursor:pointer}.ds-menu__panel{background:var(--menu-bg);border:1px solid var(--menu-border);border-radius:var(--menu-radius);box-shadow:var(--menu-shadow);padding:var(--menu-padding);min-width:var(--menu-min-width);max-width:var(--menu-max-width)}.ds-menu__panel--sm{--menu-item-height: var(--menu-item-height-sm);--menu-font-size: var(--menu-font-size-sm)}.ds-menu__panel--md{--menu-item-height: var(--menu-item-height-md);--menu-font-size: var(--menu-font-size-md)}.ds-menu__panel--lg{--menu-item-height: var(--menu-item-height-lg);--menu-font-size: var(--menu-font-size-lg)}.ds-menu__item{display:flex;align-items:center;width:100%;min-height:var(--menu-item-height);padding:var(--menu-item-padding-y) var(--menu-item-padding-x);gap:var(--menu-item-gap);border:none;border-radius:var(--menu-item-radius);background:transparent;color:var(--menu-item-text);font-size:var(--menu-font-size);font-family:var(--font-family-base);cursor:pointer;text-align:left;transition:background-color var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ds-menu__item:hover:not(.ds-menu__item--disabled),.ds-menu__item--active:not(.ds-menu__item--disabled){background:var(--menu-item-hover-bg);color:var(--menu-item-hover-text)}.ds-menu__item:focus-visible{outline:2px solid var(--focus-color);outline-offset:-2px}.ds-menu__item--disabled{color:var(--menu-item-disabled-text);cursor:not-allowed}.ds-menu__item--disabled .ds-menu__item-icon{color:var(--menu-item-disabled-icon)}.ds-menu__item-icon{flex-shrink:0;color:var(--menu-item-icon);font-size:1em}.ds-menu__item-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-menu__divider{height:1px;margin:var(--menu-divider-margin) 0;background:var(--menu-divider)}::ng-deep .ds-menu-backdrop{background:var(--menu-backdrop)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }] });
7523
+ }
7524
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMenu, decorators: [{
7525
+ type: Component,
7526
+ args: [{ selector: 'ds-menu', standalone: true, imports: [CommonModule, FaIconComponent, CdkConnectedOverlay, CdkOverlayOrigin], template: "<div\n #menuTrigger\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n (click)=\"onTriggerClick($event)\"\n (contextmenu)=\"onTriggerContextMenu($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"isOpen()\"\n class=\"ds-menu__trigger\"\n>\n <ng-content select=\"[dsMenuTrigger]\"></ng-content>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'ds-menu-backdrop'\"\n (backdropClick)=\"onBackdropClick()\"\n (detach)=\"close(false)\"\n>\n <div\n [ngClass]=\"menuClasses()\"\n role=\"menu\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n (keydown)=\"onMenuKeydown($event)\"\n >\n @for (item of items(); track item.id; let index = $index) {\n @if (item.dividerBefore) {\n <div class=\"ds-menu__divider\" role=\"separator\"></div>\n }\n <button\n #menuItemRef\n type=\"button\"\n class=\"ds-menu__item\"\n [class.ds-menu__item--disabled]=\"item.disabled\"\n [class.ds-menu__item--active]=\"activeIndex() === index\"\n [attr.id]=\"'menu-item-' + item.id\"\n [attr.aria-disabled]=\"item.disabled || null\"\n [disabled]=\"item.disabled\"\n role=\"menuitem\"\n (click)=\"selectItem(item)\"\n (mouseenter)=\"onItemMouseEnter(index)\"\n >\n @if (item.icon) {\n <fa-icon [icon]=\"item.icon\" class=\"ds-menu__item-icon\"></fa-icon>\n }\n <span class=\"ds-menu__item-label\">{{ item.label }}</span>\n </button>\n }\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:inline-block}.ds-menu__trigger{display:inline-block;cursor:pointer}.ds-menu__panel{background:var(--menu-bg);border:1px solid var(--menu-border);border-radius:var(--menu-radius);box-shadow:var(--menu-shadow);padding:var(--menu-padding);min-width:var(--menu-min-width);max-width:var(--menu-max-width)}.ds-menu__panel--sm{--menu-item-height: var(--menu-item-height-sm);--menu-font-size: var(--menu-font-size-sm)}.ds-menu__panel--md{--menu-item-height: var(--menu-item-height-md);--menu-font-size: var(--menu-font-size-md)}.ds-menu__panel--lg{--menu-item-height: var(--menu-item-height-lg);--menu-font-size: var(--menu-font-size-lg)}.ds-menu__item{display:flex;align-items:center;width:100%;min-height:var(--menu-item-height);padding:var(--menu-item-padding-y) var(--menu-item-padding-x);gap:var(--menu-item-gap);border:none;border-radius:var(--menu-item-radius);background:transparent;color:var(--menu-item-text);font-size:var(--menu-font-size);font-family:var(--font-family-base);cursor:pointer;text-align:left;transition:background-color var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ds-menu__item:hover:not(.ds-menu__item--disabled),.ds-menu__item--active:not(.ds-menu__item--disabled){background:var(--menu-item-hover-bg);color:var(--menu-item-hover-text)}.ds-menu__item:focus-visible{outline:2px solid var(--focus-color);outline-offset:-2px}.ds-menu__item--disabled{color:var(--menu-item-disabled-text);cursor:not-allowed}.ds-menu__item--disabled .ds-menu__item-icon{color:var(--menu-item-disabled-icon)}.ds-menu__item-icon{flex-shrink:0;color:var(--menu-item-icon);font-size:1em}.ds-menu__item-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-menu__divider{height:1px;margin:var(--menu-divider-margin) 0;background:var(--menu-divider)}::ng-deep .ds-menu-backdrop{background:var(--menu-backdrop)}\n"] }]
7527
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], triggerElement: [{
7528
+ type: ViewChild,
7529
+ args: ['menuTrigger', { read: ElementRef }]
7530
+ }], menuItemsRef: [{
7531
+ type: ViewChildren,
7532
+ args: ['menuItemRef']
7533
+ }] } });
7534
+
7228
7535
  /*
7229
7536
  * Components barrel export
7230
7537
  */
@@ -7796,5 +8103,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
7796
8103
  * Generated bundle index. Do not edit.
7797
8104
  */
7798
8105
 
7799
- export { AUTOCOMPLETE_POSITIONS, BUTTON_APPEARANCE_OPTIONS, BUTTON_SIZE_OPTIONS, BUTTON_VARIANT_OPTIONS, DROPDOWN_POSITIONS, DsAccordion, DsAlert, DsBadge, DsBreadcrumb, DsButton, DsCard, DsCheckbox, DsCombobox, DsContainer, DsDatePicker, DsDivider, DsDropdown, DsI18nService, DsInputField, DsInputTextarea, DsModalComponent, DsPagination, DsPopover, DsPopoverComponent, DsProgressBar, DsRadioGroup, DsSearchInput, DsSelect, DsSkeleton, DsStepper, DsTable, DsTabs, DsToastComponent, DsToastContainerComponent, DsToastService, DsToggle, DsTooltip, DsTooltipComponent, IconRegistryService, POPOVER_POSITIONS, PrimitiveBadge, PrimitiveButton, PrimitiveCheckbox, PrimitiveInput, PrimitiveRadio, PrimitiveTextarea, PrimitiveToggle, TOOLTIP_POSITIONS, buildButtonArgTypes, buildButtonArgs, createSizeRender, createVariantRender };
8106
+ export { AUTOCOMPLETE_POSITIONS, BUTTON_APPEARANCE_OPTIONS, BUTTON_SIZE_OPTIONS, BUTTON_VARIANT_OPTIONS, DROPDOWN_POSITIONS, DsAccordion, DsAlert, DsAvatar, DsBadge, DsBreadcrumb, DsButton, DsCard, DsCheckbox, DsCombobox, DsContainer, DsDatePicker, DsDivider, DsDropdown, DsI18nService, DsInputField, DsInputTextarea, DsMenu, DsModalComponent, DsPagination, DsPopover, DsPopoverComponent, DsProgressBar, DsRadioGroup, DsSearchInput, DsSelect, DsSkeleton, DsStepper, DsTable, DsTabs, DsToastComponent, DsToastContainerComponent, DsToastService, DsToggle, DsTooltip, DsTooltipComponent, IconRegistryService, POPOVER_POSITIONS, PrimitiveBadge, PrimitiveButton, PrimitiveCheckbox, PrimitiveInput, PrimitiveRadio, PrimitiveTextarea, PrimitiveToggle, TOOLTIP_POSITIONS, buildButtonArgTypes, buildButtonArgs, createSizeRender, createVariantRender };
7800
8107
  //# sourceMappingURL=kksdev-ds-angular.mjs.map