@crowdfarming/oliva-ds 1.33.0-rc.2 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Input, Component, EventEmitter, Output, input, computed, output, signal, effect, forwardRef, inject, ElementRef,
|
|
2
|
+
import { Input, Component, EventEmitter, Output, input, computed, output, signal, effect, forwardRef, inject, ElementRef, PLATFORM_ID, HostListener, ViewChild, Directive, viewChild, ApplicationRef, Injector, createComponent, Injectable, model, HostBinding } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
|
-
import { CommonModule, NgClass, isPlatformBrowser, NgStyle
|
|
4
|
+
import { CommonModule, NgClass, isPlatformBrowser, NgStyle } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/platform-browser';
|
|
6
6
|
import * as i1$2 from '@angular/router';
|
|
7
|
-
import { RouterModule,
|
|
7
|
+
import { RouterModule, RouterLink } from '@angular/router';
|
|
8
8
|
import * as i1$3 from '@angular/forms';
|
|
9
9
|
import { NG_VALUE_ACCESSOR, ReactiveFormsModule, FormControl, FormsModule } from '@angular/forms';
|
|
10
|
-
import {
|
|
11
|
-
import { FocusTrapFactory } from '@angular/cdk/a11y';
|
|
10
|
+
import { ComponentPortal } from '@angular/cdk/portal';
|
|
12
11
|
import * as i1$4 from '@angular/cdk/overlay';
|
|
13
12
|
|
|
14
13
|
// Auto-generated icons from Figma
|
|
@@ -3135,39 +3134,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImpor
|
|
|
3135
3134
|
}] });
|
|
3136
3135
|
|
|
3137
3136
|
class OverlayComponent {
|
|
3138
|
-
|
|
3139
|
-
static DROPDOWN_HEIGHT = 320;
|
|
3140
|
-
static DROPDOWN_GAP = 2;
|
|
3141
|
-
static VIEWPORT_MARGIN = 16;
|
|
3142
|
-
static ANIMATION_DURATION = 20;
|
|
3143
|
-
static DEFAULT_MENU_WIDTH = 200;
|
|
3137
|
+
// Inputs
|
|
3144
3138
|
position = input('right');
|
|
3145
3139
|
disabled = input(false);
|
|
3146
|
-
width = input(
|
|
3140
|
+
width = input('200px');
|
|
3141
|
+
// Outputs
|
|
3147
3142
|
opened = output();
|
|
3148
3143
|
closed = output();
|
|
3144
|
+
// Private signals
|
|
3149
3145
|
_isOpen = signal(false);
|
|
3150
3146
|
_isClosing = signal(false);
|
|
3151
3147
|
_isMobile = signal(false);
|
|
3152
|
-
|
|
3153
|
-
top: 0,
|
|
3154
|
-
left: 0,
|
|
3155
|
-
width: OverlayComponent.DEFAULT_MENU_WIDTH,
|
|
3156
|
-
});
|
|
3148
|
+
// Public computed signals
|
|
3157
3149
|
isOpen = this._isOpen.asReadonly();
|
|
3158
3150
|
isClosing = this._isClosing.asReadonly();
|
|
3159
|
-
|
|
3160
|
-
getMenuStyles = computed(() => {
|
|
3161
|
-
if (this._isMobile()) {
|
|
3162
|
-
return {};
|
|
3163
|
-
}
|
|
3164
|
-
const pos = this._menuPosition();
|
|
3165
|
-
return {
|
|
3166
|
-
width: `${pos.width}px`,
|
|
3167
|
-
top: `${pos.top}px`,
|
|
3168
|
-
left: `${pos.left}px`,
|
|
3169
|
-
};
|
|
3170
|
-
});
|
|
3151
|
+
// Computed class methods
|
|
3171
3152
|
getClasses = computed(() => {
|
|
3172
3153
|
const classes = ['c-overlay'];
|
|
3173
3154
|
if (this.isOpen()) {
|
|
@@ -3182,540 +3163,105 @@ class OverlayComponent {
|
|
|
3182
3163
|
}
|
|
3183
3164
|
return classes.join(' ');
|
|
3184
3165
|
});
|
|
3166
|
+
getMenuClasses = computed(() => {
|
|
3167
|
+
const classes = ['c-overlay__menu'];
|
|
3168
|
+
classes.push(`c-overlay__menu--${this.position()}`);
|
|
3169
|
+
return classes.join(' ');
|
|
3170
|
+
});
|
|
3171
|
+
getMenuStyles = computed(() => {
|
|
3172
|
+
return {
|
|
3173
|
+
width: this._isMobile() ? '100%' : this.width(),
|
|
3174
|
+
};
|
|
3175
|
+
});
|
|
3176
|
+
// Element references
|
|
3185
3177
|
elementRef = inject(ElementRef);
|
|
3186
|
-
viewContainerRef = inject(ViewContainerRef);
|
|
3187
|
-
applicationRef = inject(ApplicationRef);
|
|
3188
|
-
platformId = inject(PLATFORM_ID);
|
|
3189
|
-
destroyRef = inject(DestroyRef);
|
|
3190
|
-
focusTrapFactory = inject(FocusTrapFactory);
|
|
3191
|
-
router = inject(Router);
|
|
3192
|
-
// Portal and container management
|
|
3193
3178
|
triggerButtonRef;
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
portalContainer;
|
|
3197
|
-
boundPortalClickHandler;
|
|
3198
|
-
// Focus and accessibility management
|
|
3199
|
-
focusTrap;
|
|
3200
|
-
previouslyFocusedElement;
|
|
3201
|
-
triggerKeydownHandler;
|
|
3202
|
-
inertElements = [];
|
|
3203
|
-
escapeKeyHandler;
|
|
3204
|
-
// Scroll and navigation management
|
|
3205
|
-
originalBodyOverflow;
|
|
3206
|
-
navigationSub;
|
|
3207
|
-
// Helper to get the correct document context (important for Storybook)
|
|
3208
|
-
getDocument() {
|
|
3209
|
-
return this.elementRef.nativeElement.ownerDocument || document;
|
|
3210
|
-
}
|
|
3211
|
-
menuTemplate;
|
|
3212
|
-
contentTemplate;
|
|
3213
|
-
footerTemplate;
|
|
3179
|
+
platformId = inject(PLATFORM_ID);
|
|
3180
|
+
overlayMenu;
|
|
3214
3181
|
constructor() {
|
|
3182
|
+
// Initialize mobile detection
|
|
3215
3183
|
this.updateMobileDetection();
|
|
3184
|
+
// Only add resize listener if running in browser (not in SSR)
|
|
3216
3185
|
if (isPlatformBrowser(this.platformId)) {
|
|
3217
3186
|
window.addEventListener('resize', () => this.updateMobileDetection());
|
|
3218
3187
|
}
|
|
3188
|
+
// Emit events when overlay state changes
|
|
3219
3189
|
effect(() => {
|
|
3220
|
-
if (this.isOpen())
|
|
3190
|
+
if (this.isOpen()) {
|
|
3221
3191
|
this.opened.emit();
|
|
3222
|
-
|
|
3192
|
+
}
|
|
3193
|
+
else if (!this.isOpen() && !this.isClosing()) {
|
|
3223
3194
|
this.closed.emit();
|
|
3224
|
-
});
|
|
3225
|
-
this.destroyRef.onDestroy(() => {
|
|
3226
|
-
if (this.isOpen()) {
|
|
3227
|
-
this.unblockBodyScroll();
|
|
3228
3195
|
}
|
|
3229
|
-
this.destroyFocusTrap();
|
|
3230
|
-
this.cleanupTriggerListener();
|
|
3231
|
-
this.navigationSub?.unsubscribe();
|
|
3232
|
-
this.removeEscapeKeyListener();
|
|
3233
3196
|
});
|
|
3234
3197
|
}
|
|
3235
|
-
//
|
|
3198
|
+
// Mobile detection method
|
|
3236
3199
|
updateMobileDetection() {
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3200
|
+
// Check if running in browser (not in SSR)
|
|
3201
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
3202
|
+
this._isMobile.set(window.innerWidth <= 768);
|
|
3203
|
+
}
|
|
3204
|
+
else {
|
|
3205
|
+
// Default to desktop view during SSR
|
|
3206
|
+
this._isMobile.set(false);
|
|
3207
|
+
}
|
|
3240
3208
|
}
|
|
3241
|
-
//
|
|
3209
|
+
// Public methods for trigger directive
|
|
3242
3210
|
setTriggerButton(element) {
|
|
3243
3211
|
this.triggerButtonRef = element;
|
|
3244
|
-
this.triggerKeydownHandler = (event) => {
|
|
3245
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
3246
|
-
event.preventDefault();
|
|
3247
|
-
this.toggleOverlay();
|
|
3248
|
-
}
|
|
3249
|
-
};
|
|
3250
|
-
element.addEventListener('keydown', this.triggerKeydownHandler);
|
|
3251
3212
|
}
|
|
3252
3213
|
toggleOverlay() {
|
|
3253
|
-
if (this.disabled())
|
|
3214
|
+
if (this.disabled()) {
|
|
3254
3215
|
return;
|
|
3255
|
-
|
|
3216
|
+
}
|
|
3217
|
+
if (this.isOpen() && !this.isClosing()) {
|
|
3256
3218
|
this.closeOverlay();
|
|
3257
|
-
|
|
3219
|
+
}
|
|
3220
|
+
else if (!this.isOpen() && !this.isClosing()) {
|
|
3258
3221
|
this.openOverlay();
|
|
3222
|
+
}
|
|
3259
3223
|
}
|
|
3260
|
-
onBackdropClick() {
|
|
3261
|
-
this.closeOverlay();
|
|
3262
|
-
}
|
|
3263
|
-
onEscapeKey() {
|
|
3264
|
-
this.closeOverlay();
|
|
3265
|
-
}
|
|
3266
|
-
// ================== OVERLAY LIFECYCLE ==================
|
|
3267
3224
|
openOverlay() {
|
|
3268
|
-
this.initializeOverlayState();
|
|
3269
|
-
this.prepareOverlayForOpening();
|
|
3270
|
-
this.createOverlayPortal();
|
|
3271
|
-
this.configureScrollBehavior();
|
|
3272
|
-
this.finalizeOverlayOpening();
|
|
3273
|
-
setTimeout(() => {
|
|
3274
|
-
this.trapFocus();
|
|
3275
|
-
this.makeOutsideElementsInert();
|
|
3276
|
-
this.setupEscapeKeyListener();
|
|
3277
|
-
}, 0);
|
|
3278
|
-
}
|
|
3279
|
-
initializeOverlayState() {
|
|
3280
3225
|
this._isClosing.set(false);
|
|
3281
|
-
this.updateMobileDetection();
|
|
3282
|
-
this.previouslyFocusedElement = this.triggerButtonRef ?? this.getDocument().activeElement;
|
|
3283
|
-
}
|
|
3284
|
-
prepareOverlayForOpening() {
|
|
3285
|
-
if (!this._isMobile()) {
|
|
3286
|
-
this.calculatePosition();
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
createOverlayPortal() {
|
|
3290
|
-
this.createPortal();
|
|
3291
|
-
}
|
|
3292
|
-
configureScrollBehavior() {
|
|
3293
|
-
// Block scroll for both mobile and desktop
|
|
3294
|
-
this.blockBodyScroll();
|
|
3295
|
-
}
|
|
3296
|
-
finalizeOverlayOpening() {
|
|
3297
3226
|
this._isOpen.set(true);
|
|
3298
|
-
this.addResizeListener();
|
|
3299
|
-
this.listenToNavigation();
|
|
3300
3227
|
}
|
|
3301
3228
|
closeOverlay() {
|
|
3302
|
-
this.initializeOverlayClosing();
|
|
3303
|
-
this.cleanupOverlayResources();
|
|
3304
|
-
this.restoreScrollBehavior();
|
|
3305
|
-
this.destroyOverlayComponents();
|
|
3306
|
-
// Restore focus AFTER destroying DOM to avoid interference
|
|
3307
|
-
requestAnimationFrame(() => {
|
|
3308
|
-
this.restoreFocusToTrigger();
|
|
3309
|
-
});
|
|
3310
|
-
this.scheduleOverlayFinalization();
|
|
3311
|
-
}
|
|
3312
|
-
initializeOverlayClosing() {
|
|
3313
3229
|
this._isClosing.set(true);
|
|
3314
|
-
}
|
|
3315
|
-
cleanupOverlayResources() {
|
|
3316
|
-
this.removeResizeListener();
|
|
3317
|
-
this.unsubscribeFromNavigation();
|
|
3318
|
-
}
|
|
3319
|
-
unsubscribeFromNavigation() {
|
|
3320
|
-
this.navigationSub?.unsubscribe();
|
|
3321
|
-
this.navigationSub = undefined;
|
|
3322
|
-
}
|
|
3323
|
-
restoreScrollBehavior() {
|
|
3324
|
-
// Unblock scroll for both mobile and desktop
|
|
3325
|
-
this.unblockBodyScroll();
|
|
3326
|
-
}
|
|
3327
|
-
destroyOverlayComponents() {
|
|
3328
|
-
this.destroyFocusTrap();
|
|
3329
|
-
this.destroyPortal();
|
|
3330
|
-
// Ensure elements are restored after destroying components
|
|
3331
|
-
this.restoreOutsideElements();
|
|
3332
|
-
}
|
|
3333
|
-
scheduleOverlayFinalization() {
|
|
3334
3230
|
setTimeout(() => {
|
|
3335
|
-
this.
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
finalizeOverlayClosing() {
|
|
3339
|
-
this._isOpen.set(false);
|
|
3340
|
-
this._isClosing.set(false);
|
|
3231
|
+
this._isOpen.set(false);
|
|
3232
|
+
this._isClosing.set(false);
|
|
3233
|
+
}, 20); // Match animation duration
|
|
3341
3234
|
}
|
|
3342
|
-
//
|
|
3343
|
-
|
|
3344
|
-
if (!this.isOpen() || this.isClosing())
|
|
3345
|
-
return;
|
|
3346
|
-
const target = event.target;
|
|
3347
|
-
const onTrigger = this.triggerButtonRef?.contains(target);
|
|
3348
|
-
if (onTrigger) {
|
|
3349
|
-
return;
|
|
3350
|
-
}
|
|
3351
|
-
const menuEl = this.portalContainer?.querySelector('.c-overlay__menu');
|
|
3352
|
-
const insideMenu = menuEl?.contains(target);
|
|
3353
|
-
if (insideMenu) {
|
|
3354
|
-
return;
|
|
3355
|
-
}
|
|
3235
|
+
// Backdrop and keyboard handlers
|
|
3236
|
+
onBackdropClick() {
|
|
3356
3237
|
this.closeOverlay();
|
|
3357
|
-
};
|
|
3358
|
-
// ================== POSITIONING ==================
|
|
3359
|
-
calculatePosition() {
|
|
3360
|
-
if (!this.triggerButtonRef)
|
|
3361
|
-
return;
|
|
3362
|
-
const rect = this.triggerButtonRef.getBoundingClientRect();
|
|
3363
|
-
const menuWidth = this.parseWidth(rect.width);
|
|
3364
|
-
const menuHeight = OverlayComponent.DROPDOWN_HEIGHT;
|
|
3365
|
-
const viewportPosition = this.calculateViewportPosition(rect, menuWidth, menuHeight);
|
|
3366
|
-
const finalPosition = this.convertToFinalCoordinates(viewportPosition, menuWidth, menuHeight);
|
|
3367
|
-
this._menuPosition.set(finalPosition);
|
|
3368
|
-
}
|
|
3369
|
-
calculateViewportPosition(rect, menuWidth, menuHeight) {
|
|
3370
|
-
const gap = OverlayComponent.DROPDOWN_GAP;
|
|
3371
|
-
const viewportTop = this.calculateVerticalPosition(rect, menuHeight, gap);
|
|
3372
|
-
const viewportLeft = this.calculateHorizontalPosition(rect, menuWidth);
|
|
3373
|
-
return { top: viewportTop, left: viewportLeft };
|
|
3374
|
-
}
|
|
3375
|
-
calculateVerticalPosition(rect, menuHeight, gap) {
|
|
3376
|
-
let top = rect.bottom + gap;
|
|
3377
|
-
if (window.innerHeight - rect.bottom < menuHeight &&
|
|
3378
|
-
rect.top > rect.bottom) {
|
|
3379
|
-
top = rect.top - menuHeight - gap;
|
|
3380
|
-
}
|
|
3381
|
-
return top;
|
|
3382
|
-
}
|
|
3383
|
-
calculateHorizontalPosition(rect, menuWidth) {
|
|
3384
|
-
const position = this.position();
|
|
3385
|
-
switch (position) {
|
|
3386
|
-
case 'right':
|
|
3387
|
-
return rect.right - menuWidth;
|
|
3388
|
-
case 'center': {
|
|
3389
|
-
const triggerCenter = rect.left + rect.width / 2;
|
|
3390
|
-
return triggerCenter;
|
|
3391
|
-
}
|
|
3392
|
-
case 'left':
|
|
3393
|
-
default:
|
|
3394
|
-
return rect.left;
|
|
3395
|
-
}
|
|
3396
|
-
}
|
|
3397
|
-
convertToFinalCoordinates(viewportPosition, menuWidth, menuHeight) {
|
|
3398
|
-
const scrollTop = window.pageYOffset || this.getDocument().documentElement.scrollTop;
|
|
3399
|
-
const scrollLeft = window.pageXOffset || this.getDocument().documentElement.scrollLeft;
|
|
3400
|
-
let top;
|
|
3401
|
-
let left;
|
|
3402
|
-
if (this._isMobile()) {
|
|
3403
|
-
const clampedPosition = this.clampToViewport(viewportPosition, menuWidth, menuHeight);
|
|
3404
|
-
top = clampedPosition.top;
|
|
3405
|
-
left = clampedPosition.left;
|
|
3406
|
-
}
|
|
3407
|
-
else {
|
|
3408
|
-
const clampedPosition = this.clampToViewport(viewportPosition, menuWidth, menuHeight);
|
|
3409
|
-
top = clampedPosition.top + scrollTop;
|
|
3410
|
-
left = clampedPosition.left + scrollLeft;
|
|
3411
|
-
}
|
|
3412
|
-
return { top, left, width: menuWidth };
|
|
3413
|
-
}
|
|
3414
|
-
clampToViewport(position, menuWidth, menuHeight) {
|
|
3415
|
-
const clampedTop = Math.max(OverlayComponent.VIEWPORT_MARGIN, Math.min(position.top, window.innerHeight - menuHeight - OverlayComponent.VIEWPORT_MARGIN));
|
|
3416
|
-
const clampedLeft = Math.max(OverlayComponent.VIEWPORT_MARGIN, Math.min(position.left, window.innerWidth - menuWidth - OverlayComponent.VIEWPORT_MARGIN));
|
|
3417
|
-
return { top: clampedTop, left: clampedLeft };
|
|
3418
|
-
}
|
|
3419
|
-
parseWidth(triggerWidth) {
|
|
3420
|
-
if (this.width() === 'stretch')
|
|
3421
|
-
return triggerWidth;
|
|
3422
|
-
const w = parseFloat(this.width());
|
|
3423
|
-
return isNaN(w) ? OverlayComponent.DEFAULT_MENU_WIDTH : w;
|
|
3424
|
-
}
|
|
3425
|
-
// ================== RESIZE HANDLING ==================
|
|
3426
|
-
addResizeListener() {
|
|
3427
|
-
if (!isPlatformBrowser(this.platformId))
|
|
3428
|
-
return;
|
|
3429
|
-
window.addEventListener('resize', this.resizeHandler);
|
|
3430
|
-
}
|
|
3431
|
-
removeResizeListener() {
|
|
3432
|
-
if (!isPlatformBrowser(this.platformId))
|
|
3433
|
-
return;
|
|
3434
|
-
window.removeEventListener('resize', this.resizeHandler);
|
|
3435
|
-
}
|
|
3436
|
-
resizeHandler = () => {
|
|
3437
|
-
if (!this.isOpen())
|
|
3438
|
-
return;
|
|
3439
|
-
const wasMobile = this._isMobile();
|
|
3440
|
-
this.updateMobileDetection();
|
|
3441
|
-
const isNowMobile = this._isMobile();
|
|
3442
|
-
if (wasMobile !== isNowMobile) {
|
|
3443
|
-
this.destroyPortal();
|
|
3444
|
-
this.createPortal();
|
|
3445
|
-
if (!isNowMobile && this.triggerButtonRef) {
|
|
3446
|
-
this.calculatePosition();
|
|
3447
|
-
}
|
|
3448
|
-
}
|
|
3449
|
-
else if (!isNowMobile && this.triggerButtonRef) {
|
|
3450
|
-
this.calculatePosition();
|
|
3451
|
-
}
|
|
3452
|
-
};
|
|
3453
|
-
// ================== PORTAL MANAGEMENT ==================
|
|
3454
|
-
createPortal() {
|
|
3455
|
-
if (!this.menuTemplate || !this.contentTemplate)
|
|
3456
|
-
return;
|
|
3457
|
-
this.createPortalTemplate();
|
|
3458
|
-
this.createPortalContainer();
|
|
3459
|
-
this.setupPortalEventListeners();
|
|
3460
|
-
this.createPortalOutlet();
|
|
3461
|
-
this.attachPortalContent();
|
|
3462
|
-
// Focus trap will be set up in openOverlay()
|
|
3463
|
-
this.applyMenuPositioning();
|
|
3464
|
-
}
|
|
3465
|
-
createPortalTemplate() {
|
|
3466
|
-
this.portal = new TemplatePortal(this.menuTemplate, this.viewContainerRef);
|
|
3467
|
-
}
|
|
3468
|
-
createPortalContainer() {
|
|
3469
|
-
this.portalContainer = this.getDocument().createElement('div');
|
|
3470
|
-
this.configurePortalContainerStyles();
|
|
3471
|
-
this.getDocument().body.appendChild(this.portalContainer);
|
|
3472
|
-
}
|
|
3473
|
-
configurePortalContainerStyles() {
|
|
3474
|
-
if (!this.portalContainer)
|
|
3475
|
-
return;
|
|
3476
|
-
this.portalContainer.style.position = this._isMobile() ? 'fixed' : 'absolute';
|
|
3477
|
-
this.portalContainer.style.top = '0';
|
|
3478
|
-
this.portalContainer.style.left = '0';
|
|
3479
|
-
this.portalContainer.style.width = '100%';
|
|
3480
|
-
this.portalContainer.style.height = '100%';
|
|
3481
|
-
this.portalContainer.style.zIndex = '1000';
|
|
3482
|
-
this.portalContainer.style.pointerEvents = 'auto';
|
|
3483
|
-
}
|
|
3484
|
-
setupPortalEventListeners() {
|
|
3485
|
-
if (!this.portalContainer)
|
|
3486
|
-
return;
|
|
3487
|
-
this.boundPortalClickHandler = this.onPortalClick.bind(this);
|
|
3488
|
-
this.portalContainer.addEventListener('click', this.boundPortalClickHandler);
|
|
3489
3238
|
}
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
return;
|
|
3493
|
-
this.portalOutlet = new DomPortalOutlet(this.portalContainer, null, this.applicationRef, this.viewContainerRef.injector);
|
|
3494
|
-
}
|
|
3495
|
-
attachPortalContent() {
|
|
3496
|
-
if (!this.portalOutlet || !this.portal)
|
|
3497
|
-
return;
|
|
3498
|
-
this.portalOutlet.attach(this.portal);
|
|
3499
|
-
}
|
|
3500
|
-
applyMenuPositioning() {
|
|
3501
|
-
const menuEl = this.portalContainer?.querySelector('.c-overlay__menu');
|
|
3502
|
-
if (!menuEl)
|
|
3503
|
-
return;
|
|
3504
|
-
this.configureMenuElement(menuEl);
|
|
3505
|
-
this.applyPositioningStyles(menuEl);
|
|
3506
|
-
}
|
|
3507
|
-
configureMenuElement(menuEl) {
|
|
3508
|
-
menuEl.style.pointerEvents = 'auto';
|
|
3509
|
-
}
|
|
3510
|
-
applyPositioningStyles(menuEl) {
|
|
3511
|
-
if (this._isMobile()) {
|
|
3512
|
-
this.applyMobilePositioning(menuEl);
|
|
3513
|
-
}
|
|
3514
|
-
else {
|
|
3515
|
-
this.applyDesktopPositioning(menuEl);
|
|
3516
|
-
}
|
|
3517
|
-
}
|
|
3518
|
-
applyMobilePositioning(menuEl) {
|
|
3519
|
-
menuEl.style.position = '';
|
|
3520
|
-
menuEl.style.top = '';
|
|
3521
|
-
menuEl.style.left = '';
|
|
3522
|
-
menuEl.style.width = '';
|
|
3523
|
-
}
|
|
3524
|
-
applyDesktopPositioning(menuEl) {
|
|
3525
|
-
const pos = this._menuPosition();
|
|
3526
|
-
menuEl.style.position = 'absolute';
|
|
3527
|
-
menuEl.style.top = `${pos.top}px`;
|
|
3528
|
-
menuEl.style.left = `${pos.left}px`;
|
|
3529
|
-
menuEl.style.width = `${pos.width}px`;
|
|
3530
|
-
}
|
|
3531
|
-
destroyPortal() {
|
|
3532
|
-
if (this.portalOutlet) {
|
|
3533
|
-
this.portalOutlet.detach();
|
|
3534
|
-
this.portalOutlet.dispose();
|
|
3535
|
-
this.portalOutlet = undefined;
|
|
3536
|
-
this.portal = undefined;
|
|
3537
|
-
}
|
|
3538
|
-
if (this.portalContainer && this.portalContainer.parentNode) {
|
|
3539
|
-
if (this.boundPortalClickHandler) {
|
|
3540
|
-
this.portalContainer.removeEventListener('click', this.boundPortalClickHandler);
|
|
3541
|
-
this.boundPortalClickHandler = undefined;
|
|
3542
|
-
}
|
|
3543
|
-
this.portalContainer.parentNode.removeChild(this.portalContainer);
|
|
3544
|
-
}
|
|
3545
|
-
this.portalContainer = undefined;
|
|
3546
|
-
}
|
|
3547
|
-
// ================== SCROLL MANAGEMENT ==================
|
|
3548
|
-
blockBodyScroll() {
|
|
3549
|
-
if (!isPlatformBrowser(this.platformId))
|
|
3550
|
-
return;
|
|
3551
|
-
this.originalBodyOverflow = this.getDocument().body.style.overflow;
|
|
3552
|
-
this.getDocument().body.style.overflow = 'hidden';
|
|
3553
|
-
this.getDocument().body.style.position = 'fixed';
|
|
3554
|
-
this.getDocument().body.style.width = '100%';
|
|
3555
|
-
this.getDocument().body.style.top = `-${window.pageYOffset}px`;
|
|
3556
|
-
this.getDocument().documentElement.style.overflow = 'hidden';
|
|
3557
|
-
}
|
|
3558
|
-
unblockBodyScroll() {
|
|
3559
|
-
if (!isPlatformBrowser(this.platformId))
|
|
3560
|
-
return;
|
|
3561
|
-
const scrollY = this.getDocument().body.style.top;
|
|
3562
|
-
if (this.originalBodyOverflow !== undefined) {
|
|
3563
|
-
this.getDocument().body.style.overflow = this.originalBodyOverflow;
|
|
3564
|
-
}
|
|
3565
|
-
else {
|
|
3566
|
-
this.getDocument().body.style.overflow = '';
|
|
3567
|
-
}
|
|
3568
|
-
this.getDocument().body.style.position = '';
|
|
3569
|
-
this.getDocument().body.style.width = '';
|
|
3570
|
-
this.getDocument().body.style.top = '';
|
|
3571
|
-
this.getDocument().documentElement.style.overflow = '';
|
|
3572
|
-
if (scrollY) {
|
|
3573
|
-
window.scrollTo(0, parseInt(scrollY || '0') * -1);
|
|
3574
|
-
}
|
|
3575
|
-
this.originalBodyOverflow = undefined;
|
|
3576
|
-
}
|
|
3577
|
-
// ================== FOCUS MANAGEMENT ==================
|
|
3578
|
-
trapFocus() {
|
|
3579
|
-
if (!this.portalContainer) {
|
|
3580
|
-
return;
|
|
3581
|
-
}
|
|
3582
|
-
// Search for menu element in entire document, not just within portalContainer
|
|
3583
|
-
const doc = this.getDocument();
|
|
3584
|
-
const menuEl = doc.querySelector('.c-overlay__menu');
|
|
3585
|
-
if (!menuEl) {
|
|
3586
|
-
setTimeout(() => this.trapFocus(), 10);
|
|
3587
|
-
return;
|
|
3588
|
-
}
|
|
3589
|
-
// CDK FocusTrap
|
|
3590
|
-
this.focusTrap = this.focusTrapFactory.create(menuEl);
|
|
3591
|
-
this.focusTrap.focusInitialElement();
|
|
3239
|
+
onEscapeKey() {
|
|
3240
|
+
this.closeOverlay();
|
|
3592
3241
|
}
|
|
3593
|
-
//
|
|
3594
|
-
|
|
3595
|
-
if (!
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3242
|
+
// Host listeners
|
|
3243
|
+
onClickOutside(event) {
|
|
3244
|
+
if (this.isOpen() && !this.isClosing()) {
|
|
3245
|
+
const clickedElement = event.target;
|
|
3246
|
+
const isClickInsideOverlay = this.elementRef.nativeElement.contains(clickedElement);
|
|
3247
|
+
const isClickOnTrigger = this.triggerButtonRef?.contains(clickedElement);
|
|
3248
|
+
if (!isClickInsideOverlay && !isClickOnTrigger) {
|
|
3600
3249
|
this.closeOverlay();
|
|
3601
3250
|
}
|
|
3602
|
-
};
|
|
3603
|
-
this.getDocument().addEventListener('keydown', this.escapeKeyHandler);
|
|
3604
|
-
}
|
|
3605
|
-
removeEscapeKeyListener() {
|
|
3606
|
-
if (!isPlatformBrowser(this.platformId))
|
|
3607
|
-
return;
|
|
3608
|
-
if (this.escapeKeyHandler) {
|
|
3609
|
-
this.getDocument().removeEventListener('keydown', this.escapeKeyHandler);
|
|
3610
|
-
this.escapeKeyHandler = undefined;
|
|
3611
|
-
}
|
|
3612
|
-
}
|
|
3613
|
-
// ================== FOCUS RESTORATION ==================
|
|
3614
|
-
restoreFocusToTrigger() {
|
|
3615
|
-
const doc = this.getDocument();
|
|
3616
|
-
// Prefer to return focus to trigger button
|
|
3617
|
-
if (this.triggerButtonRef && doc.contains(this.triggerButtonRef)) {
|
|
3618
|
-
const focusableElement = this.findFocusableButton(this.triggerButtonRef);
|
|
3619
|
-
if (focusableElement) {
|
|
3620
|
-
focusableElement.focus({ preventScroll: true });
|
|
3621
|
-
return;
|
|
3622
|
-
}
|
|
3623
|
-
}
|
|
3624
|
-
// Si no hay trigger, devolvemos el foco al elemento que estaba activo antes
|
|
3625
|
-
if (this.previouslyFocusedElement && doc.contains(this.previouslyFocusedElement)) {
|
|
3626
|
-
const focusableElement = this.findFocusableButton(this.previouslyFocusedElement);
|
|
3627
|
-
if (focusableElement) {
|
|
3628
|
-
focusableElement.focus({ preventScroll: true });
|
|
3629
|
-
return;
|
|
3630
|
-
}
|
|
3631
3251
|
}
|
|
3632
|
-
// Safety fallback
|
|
3633
|
-
doc.body.focus();
|
|
3634
|
-
}
|
|
3635
|
-
findFocusableButton(element) {
|
|
3636
|
-
// If element is already a focusable button, return it
|
|
3637
|
-
if (element.tagName === 'BUTTON' && !element.hasAttribute('disabled')) {
|
|
3638
|
-
return element;
|
|
3639
|
-
}
|
|
3640
|
-
// If it's a lib-button or custom component, search for internal button
|
|
3641
|
-
const button = element.querySelector('button:not([disabled])');
|
|
3642
|
-
if (button) {
|
|
3643
|
-
return button;
|
|
3644
|
-
}
|
|
3645
|
-
// Search for any focusable element inside
|
|
3646
|
-
const focusableSelectors = [
|
|
3647
|
-
'button:not([disabled])',
|
|
3648
|
-
'input:not([disabled])',
|
|
3649
|
-
'select:not([disabled])',
|
|
3650
|
-
'textarea:not([disabled])',
|
|
3651
|
-
'a[href]',
|
|
3652
|
-
'[tabindex]:not([tabindex="-1"])'
|
|
3653
|
-
];
|
|
3654
|
-
const focusable = element.querySelector(focusableSelectors.join(', '));
|
|
3655
|
-
return focusable;
|
|
3656
|
-
}
|
|
3657
|
-
releaseFocusTrap() {
|
|
3658
|
-
if (this.focusTrap) {
|
|
3659
|
-
this.focusTrap.destroy();
|
|
3660
|
-
this.focusTrap = undefined;
|
|
3661
|
-
}
|
|
3662
|
-
this.removeEscapeKeyListener();
|
|
3663
|
-
}
|
|
3664
|
-
// ================== INERT OUTSIDE ELEMENTS ==================
|
|
3665
|
-
makeOutsideElementsInert() {
|
|
3666
|
-
if (!isPlatformBrowser(this.platformId))
|
|
3667
|
-
return;
|
|
3668
|
-
this.inertElements = [];
|
|
3669
|
-
const doc = this.getDocument();
|
|
3670
|
-
const bodyChildren = Array.from(doc.body.children);
|
|
3671
|
-
bodyChildren.forEach(el => {
|
|
3672
|
-
if (el !== this.portalContainer && !el.hasAttribute('aria-hidden')) {
|
|
3673
|
-
el.inert = true; // Hace que no sean focusables ni interactuables
|
|
3674
|
-
this.inertElements.push(el);
|
|
3675
|
-
}
|
|
3676
|
-
});
|
|
3677
|
-
}
|
|
3678
|
-
restoreOutsideElements() {
|
|
3679
|
-
if (!isPlatformBrowser(this.platformId))
|
|
3680
|
-
return;
|
|
3681
|
-
this.inertElements.forEach(el => {
|
|
3682
|
-
el.inert = false;
|
|
3683
|
-
});
|
|
3684
|
-
this.inertElements = [];
|
|
3685
|
-
}
|
|
3686
|
-
destroyFocusTrap() {
|
|
3687
|
-
this.releaseFocusTrap();
|
|
3688
|
-
this.restoreOutsideElements();
|
|
3689
|
-
}
|
|
3690
|
-
cleanupTriggerListener() {
|
|
3691
|
-
if (this.triggerButtonRef && this.triggerKeydownHandler) {
|
|
3692
|
-
this.triggerButtonRef.removeEventListener('keydown', this.triggerKeydownHandler);
|
|
3693
|
-
this.triggerKeydownHandler = undefined;
|
|
3694
|
-
}
|
|
3695
|
-
}
|
|
3696
|
-
listenToNavigation() {
|
|
3697
|
-
this.navigationSub?.unsubscribe(); // Clean up previous subscription
|
|
3698
|
-
this.navigationSub = this.router.events.subscribe(event => {
|
|
3699
|
-
if (event instanceof NavigationStart && this.isOpen()) {
|
|
3700
|
-
this.closeOverlay();
|
|
3701
|
-
}
|
|
3702
|
-
});
|
|
3703
3252
|
}
|
|
3704
3253
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: OverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3705
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: OverlayComponent, isStandalone: true, selector: "lib-overlay", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened", closed: "closed" },
|
|
3254
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: OverlayComponent, isStandalone: true, selector: "lib-overlay", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened", closed: "closed" }, host: { listeners: { "document:click": "onClickOutside($event)" } }, viewQueries: [{ propertyName: "overlayMenu", first: true, predicate: ["overlayMenu"], descendants: true }], ngImport: i0, template: "<div class=\"c-overlay\" [ngClass]=\"getClasses()\">\n <!-- Backdrop for mobile -->\n @if (isOpen() || isClosing()) {\n <div\n class=\"c-overlay__backdrop\"\n [ngClass]=\"getBackdropClasses()\"\n (click)=\"onBackdropClick()\"\n (keydown.escape)=\"onEscapeKey()\"\n tabindex=\"0\"\n role=\"button\"\n aria-label=\"Close overlay\"\n ></div>\n }\n\n <!-- Overlay Menu -->\n @if (isOpen() || isClosing()) {\n <div\n class=\"c-overlay__menu\"\n [ngClass]=\"getMenuClasses()\"\n [ngStyle]=\"getMenuStyles()\"\n #overlayMenu\n >\n <div class=\"c-overlay__content\">\n <!-- Main content projection -->\n <ng-content></ng-content>\n </div>\n \n <!-- Footer slot for mobile action buttons -->\n <div class=\"c-overlay__footer\">\n <ng-content select=\"[slot=footer]\"></ng-content>\n </div>\n </div>\n }\n</div>", styles: [".c-overlay{position:relative}.c-overlay__backdrop{position:fixed;inset:0;z-index:999;background-color:var(--color-effect-overlay);opacity:0;transition:opacity .2s ease-in-out;display:none}.c-overlay__backdrop--open{opacity:1}.c-overlay__menu{position:absolute;margin-top:2px;z-index:1000;background-color:var(--color-core-background-surface-raised);border:var(--size-border-width-sm) solid var(--color-core-border-soft);border-radius:var(--size-textfield-border-radius);box-shadow:var(--elevation-raised);max-height:320px;min-width:200px;overflow:hidden;display:flex;flex-direction:column;opacity:0;transform:scale(.95);animation:overlay-enter .2s ease-out forwards;transform-origin:top center}.c-overlay__menu--right{left:0;right:auto}.c-overlay__menu--left{left:auto;right:0}.c-overlay__menu--center{left:50%;right:auto;transform:translate(-50%) scale(.95);animation:overlay-enter-center .2s ease-out forwards}.c-overlay__content{flex:1;overflow-y:auto}.c-overlay__footer{display:none;height:27px}.c-overlay__footer:not(:empty){display:flex}@keyframes overlay-enter{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes overlay-enter-center{0%{opacity:0;transform:translate(-50%) scale(.95)}to{opacity:1;transform:translate(-50%) scale(1)}}@keyframes overlay-exit{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:scale(.95)}}@media (max-width: 768px){.c-overlay__backdrop{display:block;z-index:999}.c-overlay__menu{position:fixed;bottom:0;left:0;right:0;width:100%;max-width:none;max-height:100vh;margin-top:0;border-radius:0;animation:none;z-index:1001;opacity:1;display:flex;flex-direction:column}.c-overlay__content{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 27px);width:100%;min-width:0}.c-overlay__content>*{max-width:100%!important;width:100%!important;box-sizing:border-box!important;word-wrap:break-word!important;overflow-wrap:break-word!important;overflow-x:hidden!important}.c-overlay__content *{max-width:100%;box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}.c-overlay__footer{flex-shrink:0;padding:var(--space-container-padding-sm);background-color:var(--color-core-background-surface-raised);border-top:var(--size-border-width-sm) solid var(--color-core-border-soft);display:flex;justify-content:space-between;align-items:center;height:27px}.c-overlay__footer:empty{display:none}.c-overlay--open .c-overlay__menu{transform:translateY(0)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
|
|
3706
3255
|
}
|
|
3707
3256
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: OverlayComponent, decorators: [{
|
|
3708
3257
|
type: Component,
|
|
3709
|
-
args: [{ selector: 'lib-overlay', imports: [NgClass, NgStyle
|
|
3710
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
3258
|
+
args: [{ selector: 'lib-overlay', imports: [NgClass, NgStyle], template: "<div class=\"c-overlay\" [ngClass]=\"getClasses()\">\n <!-- Backdrop for mobile -->\n @if (isOpen() || isClosing()) {\n <div\n class=\"c-overlay__backdrop\"\n [ngClass]=\"getBackdropClasses()\"\n (click)=\"onBackdropClick()\"\n (keydown.escape)=\"onEscapeKey()\"\n tabindex=\"0\"\n role=\"button\"\n aria-label=\"Close overlay\"\n ></div>\n }\n\n <!-- Overlay Menu -->\n @if (isOpen() || isClosing()) {\n <div\n class=\"c-overlay__menu\"\n [ngClass]=\"getMenuClasses()\"\n [ngStyle]=\"getMenuStyles()\"\n #overlayMenu\n >\n <div class=\"c-overlay__content\">\n <!-- Main content projection -->\n <ng-content></ng-content>\n </div>\n \n <!-- Footer slot for mobile action buttons -->\n <div class=\"c-overlay__footer\">\n <ng-content select=\"[slot=footer]\"></ng-content>\n </div>\n </div>\n }\n</div>", styles: [".c-overlay{position:relative}.c-overlay__backdrop{position:fixed;inset:0;z-index:999;background-color:var(--color-effect-overlay);opacity:0;transition:opacity .2s ease-in-out;display:none}.c-overlay__backdrop--open{opacity:1}.c-overlay__menu{position:absolute;margin-top:2px;z-index:1000;background-color:var(--color-core-background-surface-raised);border:var(--size-border-width-sm) solid var(--color-core-border-soft);border-radius:var(--size-textfield-border-radius);box-shadow:var(--elevation-raised);max-height:320px;min-width:200px;overflow:hidden;display:flex;flex-direction:column;opacity:0;transform:scale(.95);animation:overlay-enter .2s ease-out forwards;transform-origin:top center}.c-overlay__menu--right{left:0;right:auto}.c-overlay__menu--left{left:auto;right:0}.c-overlay__menu--center{left:50%;right:auto;transform:translate(-50%) scale(.95);animation:overlay-enter-center .2s ease-out forwards}.c-overlay__content{flex:1;overflow-y:auto}.c-overlay__footer{display:none;height:27px}.c-overlay__footer:not(:empty){display:flex}@keyframes overlay-enter{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes overlay-enter-center{0%{opacity:0;transform:translate(-50%) scale(.95)}to{opacity:1;transform:translate(-50%) scale(1)}}@keyframes overlay-exit{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:scale(.95)}}@media (max-width: 768px){.c-overlay__backdrop{display:block;z-index:999}.c-overlay__menu{position:fixed;bottom:0;left:0;right:0;width:100%;max-width:none;max-height:100vh;margin-top:0;border-radius:0;animation:none;z-index:1001;opacity:1;display:flex;flex-direction:column}.c-overlay__content{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 27px);width:100%;min-width:0}.c-overlay__content>*{max-width:100%!important;width:100%!important;box-sizing:border-box!important;word-wrap:break-word!important;overflow-wrap:break-word!important;overflow-x:hidden!important}.c-overlay__content *{max-width:100%;box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}.c-overlay__footer{flex-shrink:0;padding:var(--space-container-padding-sm);background-color:var(--color-core-background-surface-raised);border-top:var(--size-border-width-sm) solid var(--color-core-border-soft);display:flex;justify-content:space-between;align-items:center;height:27px}.c-overlay__footer:empty{display:none}.c-overlay--open .c-overlay__menu{transform:translateY(0)}}\n"] }]
|
|
3259
|
+
}], ctorParameters: () => [], propDecorators: { overlayMenu: [{
|
|
3711
3260
|
type: ViewChild,
|
|
3712
|
-
args: ['
|
|
3713
|
-
}],
|
|
3714
|
-
type:
|
|
3715
|
-
args: [
|
|
3716
|
-
}], footerTemplate: [{
|
|
3717
|
-
type: ContentChild,
|
|
3718
|
-
args: ['footer', { static: true }]
|
|
3261
|
+
args: ['overlayMenu']
|
|
3262
|
+
}], onClickOutside: [{
|
|
3263
|
+
type: HostListener,
|
|
3264
|
+
args: ['document:click', ['$event']]
|
|
3719
3265
|
}] } });
|
|
3720
3266
|
|
|
3721
3267
|
class DropdownActionComponent {
|
|
@@ -3781,11 +3327,11 @@ class DropdownActionComponent {
|
|
|
3781
3327
|
this._isOverlayOpen.set(false);
|
|
3782
3328
|
}
|
|
3783
3329
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: DropdownActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3784
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: DropdownActionComponent, isStandalone: true, selector: "lib-dropdown-action", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionClicked: "actionClicked" }, viewQueries: [{ propertyName: "overlay", first: true, predicate: OverlayComponent, descendants: true }], ngImport: i0, template: "<lib-overlay\n [position]=\"position()\"\n [disabled]=\"disabled()\"\n (opened)=\"onOverlayOpened()\"\n (closed)=\"onOverlayClosed()\"\n [width]=\"width()\"\n>\n
|
|
3330
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: DropdownActionComponent, isStandalone: true, selector: "lib-dropdown-action", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionClicked: "actionClicked" }, viewQueries: [{ propertyName: "overlay", first: true, predicate: OverlayComponent, descendants: true }], ngImport: i0, template: "<lib-overlay\n [position]=\"position()\"\n [disabled]=\"disabled()\"\n (opened)=\"onOverlayOpened()\"\n (closed)=\"onOverlayClosed()\"\n [width]=\"width()\"\n>\n <!-- Options List -->\n <div class=\"c-dropdown-action__options\">\n @for (group of options(); track group.label) {\n <!-- Option Group -->\n <div class=\"c-dropdown-action__group\">\n <!-- Group Label -->\n @if (group.label) {\n <div class=\"c-dropdown-action__group-label\">\n {{ group.label }}\n </div>\n }\n <!-- Group Options -->\n @for (option of group.options; track option.value) {\n <!-- Action button with icon and label -->\n <button\n type=\"button\"\n class=\"c-dropdown-action__option\"\n [disabled]=\"option.disabled\"\n (click)=\"selectAction(option)\"\n >\n @if (option.icon) {\n <lib-icon\n [name]=\"option.icon\"\n size=\"lg\"\n class=\"c-dropdown-action__option-icon\"\n >\n </lib-icon>\n }\n\n <span class=\"c-dropdown-action__option-label\">\n {{ option.label }}\n </span>\n </button>\n }\n </div>\n }\n </div>\n</lib-overlay>\n", styles: [".c-dropdown-action__search{padding:var(--space-container-padding-sm);border-bottom:var(--size-border-width-sm) solid var(--color-core-border-soft)}.c-dropdown-action__options{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0}.c-dropdown-action__group-label{color:var(--color-core-content-soft);padding:var(--space-component-padding-md) var(--space-component-padding-lg);font-family:var(--typography-label-sm-strong-family),sans-serif;font-weight:var(--typography-label-sm-strong-weight);font-size:var(--typography-label-sm-strong-size);letter-spacing:var(--typography-label-sm-strong-letter-spacing)}.c-dropdown-action__option{display:flex;align-items:center;width:100%;min-height:40px;gap:var(--space-component-gap-sm);padding:var(--space-component-padding-sm) var(--space-component-padding-lg);border:none;background:var(--color-action-neutral-background-default);color:var(--color-core-content-default);font-family:var(--typography-label-sm-default-family),sans-serif;font-weight:var(--typography-label-sm-default-weight);font-size:var(--typography-label-sm-default-size);letter-spacing:var(--typography-label-sm-default-letter-spacing);text-align:left;cursor:pointer}.c-dropdown-action__option:hover:not(:disabled){background-color:var(--color-action-neutral-background-hover)}.c-dropdown-action__option:focus-visible:not(:disabled){box-shadow:var(--focus-inset);outline:none}.c-dropdown-action__option:disabled{cursor:not-allowed;opacity:.5}.c-dropdown-action__option-icon{flex-shrink:0;margin-right:12px;color:var(--color-core-content-soft)}.c-dropdown-action__option-label{flex:1;min-width:0}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["size", "icon", "name", "color"] }, { kind: "component", type: OverlayComponent, selector: "lib-overlay", inputs: ["position", "disabled", "width"], outputs: ["opened", "closed"] }] });
|
|
3785
3331
|
}
|
|
3786
3332
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: DropdownActionComponent, decorators: [{
|
|
3787
3333
|
type: Component,
|
|
3788
|
-
args: [{ selector: 'lib-dropdown-action', imports: [IconComponent, OverlayComponent], template: "<lib-overlay\n [position]=\"position()\"\n [disabled]=\"disabled()\"\n (opened)=\"onOverlayOpened()\"\n (closed)=\"onOverlayClosed()\"\n [width]=\"width()\"\n>\n
|
|
3334
|
+
args: [{ selector: 'lib-dropdown-action', imports: [IconComponent, OverlayComponent], template: "<lib-overlay\n [position]=\"position()\"\n [disabled]=\"disabled()\"\n (opened)=\"onOverlayOpened()\"\n (closed)=\"onOverlayClosed()\"\n [width]=\"width()\"\n>\n <!-- Options List -->\n <div class=\"c-dropdown-action__options\">\n @for (group of options(); track group.label) {\n <!-- Option Group -->\n <div class=\"c-dropdown-action__group\">\n <!-- Group Label -->\n @if (group.label) {\n <div class=\"c-dropdown-action__group-label\">\n {{ group.label }}\n </div>\n }\n <!-- Group Options -->\n @for (option of group.options; track option.value) {\n <!-- Action button with icon and label -->\n <button\n type=\"button\"\n class=\"c-dropdown-action__option\"\n [disabled]=\"option.disabled\"\n (click)=\"selectAction(option)\"\n >\n @if (option.icon) {\n <lib-icon\n [name]=\"option.icon\"\n size=\"lg\"\n class=\"c-dropdown-action__option-icon\"\n >\n </lib-icon>\n }\n\n <span class=\"c-dropdown-action__option-label\">\n {{ option.label }}\n </span>\n </button>\n }\n </div>\n }\n </div>\n</lib-overlay>\n", styles: [".c-dropdown-action__search{padding:var(--space-container-padding-sm);border-bottom:var(--size-border-width-sm) solid var(--color-core-border-soft)}.c-dropdown-action__options{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0}.c-dropdown-action__group-label{color:var(--color-core-content-soft);padding:var(--space-component-padding-md) var(--space-component-padding-lg);font-family:var(--typography-label-sm-strong-family),sans-serif;font-weight:var(--typography-label-sm-strong-weight);font-size:var(--typography-label-sm-strong-size);letter-spacing:var(--typography-label-sm-strong-letter-spacing)}.c-dropdown-action__option{display:flex;align-items:center;width:100%;min-height:40px;gap:var(--space-component-gap-sm);padding:var(--space-component-padding-sm) var(--space-component-padding-lg);border:none;background:var(--color-action-neutral-background-default);color:var(--color-core-content-default);font-family:var(--typography-label-sm-default-family),sans-serif;font-weight:var(--typography-label-sm-default-weight);font-size:var(--typography-label-sm-default-size);letter-spacing:var(--typography-label-sm-default-letter-spacing);text-align:left;cursor:pointer}.c-dropdown-action__option:hover:not(:disabled){background-color:var(--color-action-neutral-background-hover)}.c-dropdown-action__option:focus-visible:not(:disabled){box-shadow:var(--focus-inset);outline:none}.c-dropdown-action__option:disabled{cursor:not-allowed;opacity:.5}.c-dropdown-action__option-icon{flex-shrink:0;margin-right:12px;color:var(--color-core-content-soft)}.c-dropdown-action__option-label{flex:1;min-width:0}\n"] }]
|
|
3789
3335
|
}], ctorParameters: () => [], propDecorators: { overlay: [{
|
|
3790
3336
|
type: ViewChild,
|
|
3791
3337
|
args: [OverlayComponent]
|
|
@@ -4340,19 +3886,18 @@ class DropdownSelectComponent {
|
|
|
4340
3886
|
}
|
|
4341
3887
|
}
|
|
4342
3888
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: DropdownSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4343
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: DropdownSelectComponent, isStandalone: true, selector: "lib-dropdown-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, selectedValues: { classPropertyName: "selectedValues", publicName: "selectedValues", isSignal: true, isRequired: false, transformFunction: null }, searchBarEnabled: { classPropertyName: "searchBarEnabled", publicName: "searchBarEnabled", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, cancelButtonText: { classPropertyName: "cancelButtonText", publicName: "cancelButtonText", isSignal: true, isRequired: false, transformFunction: null }, acceptButtonText: { classPropertyName: "acceptButtonText", publicName: "acceptButtonText", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected", optionDeselected: "optionDeselected", searchChanged: "searchChanged", valueChange: "valueChange", cancelClicked: "cancelClicked", acceptClicked: "acceptClicked" }, viewQueries: [{ propertyName: "overlay", first: true, predicate: OverlayComponent, descendants: true }], ngImport: i0, template: "<lib-overlay\n [position]=\"position()\"\n [disabled]=\"disabled()\"\n (opened)=\"onOverlayOpened()\"\n (closed)=\"onOverlayClosed()\"\n [width]=\"width()\"\n>\n
|
|
3889
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: DropdownSelectComponent, isStandalone: true, selector: "lib-dropdown-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, selectedValues: { classPropertyName: "selectedValues", publicName: "selectedValues", isSignal: true, isRequired: false, transformFunction: null }, searchBarEnabled: { classPropertyName: "searchBarEnabled", publicName: "searchBarEnabled", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, cancelButtonText: { classPropertyName: "cancelButtonText", publicName: "cancelButtonText", isSignal: true, isRequired: false, transformFunction: null }, acceptButtonText: { classPropertyName: "acceptButtonText", publicName: "acceptButtonText", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected", optionDeselected: "optionDeselected", searchChanged: "searchChanged", valueChange: "valueChange", cancelClicked: "cancelClicked", acceptClicked: "acceptClicked" }, viewQueries: [{ propertyName: "overlay", first: true, predicate: OverlayComponent, descendants: true }], ngImport: i0, template: "<lib-overlay\n [position]=\"position()\"\n [disabled]=\"disabled()\"\n (opened)=\"onOverlayOpened()\"\n (closed)=\"onOverlayClosed()\"\n [width]=\"width()\"\n>\n <!-- Search Input (if enabled and not multiselect) -->\n @if (showSearchBar()) {\n <div class=\"c-dropdown-select__search\">\n <lib-input-search\n [placeholder]=\"searchPlaceholder()\"\n [formControl]=\"searchControl\"\n size=\"lg\"\n [fullWidth]=\"true\"\n ></lib-input-search>\n </div>\n }\n\n <!-- Options List -->\n <div class=\"c-dropdown-select__options\">\n @for (group of filteredOptions(); track group.label) {\n <!-- Option Group -->\n <div class=\"c-dropdown-select__group\">\n <!-- Group Label -->\n @if (group.label) {\n <div class=\"c-dropdown-select__group-label\">\n {{ group.label }}\n </div>\n }\n <!-- Group Options -->\n @for (option of group.options; track option.value) { @if (isMultiSelect())\n {\n <!-- For multiselect: use full checkbox component with label -->\n <div\n class=\"c-dropdown-select__option c-dropdown-select__option--multiselect\"\n >\n <lib-checkbox\n [label]=\"option.label\"\n [name]=\"'option-' + option.value\"\n [value]=\"option.value\"\n [ariaLabel]=\"'Select ' + option.label\"\n [state]=\"isSelected(option) ? 'checked' : 'unchecked'\"\n [disabled]=\"option.disabled || false\"\n (changed)=\"onCheckboxChange(option, $event.checked)\"\n ></lib-checkbox>\n </div>\n } @else {\n <!-- For single select: use button with icon and label -->\n <button\n type=\"button\"\n class=\"c-dropdown-select__option\"\n [class.c-dropdown-select__option--selected]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n >\n @if (option.icon) {\n <lib-icon\n [name]=\"option.icon\"\n size=\"lg\"\n class=\"c-dropdown-select__option-icon\"\n >\n </lib-icon>\n }\n\n <span class=\"c-dropdown-select__option-label\">\n {{ option.label }}\n </span>\n\n <!-- Check icon for selected option in single select -->\n @if (isSelected(option)) {\n <lib-icon\n name=\"checkcircle-fill\"\n size=\"lg\"\n color=\"action-primary-selected-background-default\"\n >\n </lib-icon>\n }\n </button>\n } }\n </div>\n }\n </div>\n\n <!-- Mobile Action Buttons in footer slot -->\n @if (showMobileActions()) {\n <div slot=\"footer\" class=\"c-dropdown-select__mobile-actions\">\n <lib-link-action\n [text]=\"cancelButtonText()\"\n variant=\"neutral\"\n size=\"md\"\n weight=\"bold\"\n (clicked)=\"onCancelClick()\"\n ></lib-link-action>\n <lib-link-action\n [text]=\"acceptButtonText()\"\n variant=\"primary\"\n size=\"md\"\n weight=\"bold\"\n (clicked)=\"onAcceptClick()\"\n ></lib-link-action>\n </div>\n }\n</lib-overlay>\n", styles: [".c-dropdown-select__search{padding:var(--space-container-padding-sm);border-bottom:var(--size-border-width-sm) solid var(--color-core-border-soft)}.c-dropdown-select__options{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0}.c-dropdown-select__group-label{color:var(--color-core-content-soft);padding:var(--space-component-padding-md) var(--space-component-padding-lg);font-family:var(--typography-label-sm-strong-family),sans-serif;font-weight:var(--typography-label-sm-strong-weight);font-size:var(--typography-label-sm-strong-size);letter-spacing:var(--typography-label-sm-strong-letter-spacing)}.c-dropdown-select__option{display:flex;align-items:center;width:100%;min-height:40px;gap:var(--space-component-gap-sm);padding:var(--space-component-padding-sm) var(--space-component-padding-lg);border:none;background:var(--color-action-neutral-background-default);color:var(--color-core-content-default);font-family:var(--typography-label-sm-default-family),sans-serif;font-weight:var(--typography-label-sm-default-weight);font-size:var(--typography-label-sm-default-size);letter-spacing:var(--typography-label-sm-default-letter-spacing);text-align:left;cursor:pointer}.c-dropdown-select__option:hover:not(:disabled){background-color:var(--color-action-neutral-background-hover)}.c-dropdown-select__option:focus-visible:not(:disabled){box-shadow:var(--focus-inset);outline:none}.c-dropdown-select__option:disabled{cursor:not-allowed;opacity:.5}.c-dropdown-select__option--multiselect{cursor:default;padding:0 var(--space-component-padding-lg)}.c-dropdown-select__option--multiselect:hover{background-color:var(--color-action-neutral-background-hover)}.c-dropdown-select__option--multiselect__option{font-family:var(--typography-label-md-default-family),sans-serif;font-weight:var(--typography-label-md-default-weight);font-size:var(--typography-label-md-default-size);letter-spacing:var(--typography-label-md-default-letter-spacing)}.c-dropdown-select__option-icon{flex-shrink:0;margin-right:12px;color:var(--color-core-content-soft)}.c-dropdown-select__option-label{flex:1;min-width:0}.c-dropdown-select__mobile-actions{display:flex;justify-content:space-between;align-items:center;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["size", "icon", "name", "color"] }, { kind: "component", type: CheckboxComponent, selector: "lib-checkbox", inputs: ["label", "state", "errorActive", "skeletonActive", "disabled", "name", "value", "innerHTML", "ariaLabel"], outputs: ["changed"] }, { kind: "component", type: InputSearchComponent, selector: "lib-input-search", inputs: ["label", "placeholder", "helperText", "alertText", "successText", "error", "success", "disabled", "readonly", "required", "size", "fullWidth", "clearButton"], outputs: ["emitValue"] }, { kind: "component", type: LinkActionComponent, selector: "lib-link-action", inputs: ["text", "disabled", "variant", "size", "weight", "iconBefore", "iconAfter"], outputs: ["clicked"] }, { kind: "component", type: OverlayComponent, selector: "lib-overlay", inputs: ["position", "disabled", "width"], outputs: ["opened", "closed"] }] });
|
|
4344
3890
|
}
|
|
4345
3891
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: DropdownSelectComponent, decorators: [{
|
|
4346
3892
|
type: Component,
|
|
4347
3893
|
args: [{ selector: 'lib-dropdown-select', imports: [
|
|
4348
3894
|
ReactiveFormsModule,
|
|
4349
|
-
NgClass,
|
|
4350
3895
|
IconComponent,
|
|
4351
3896
|
CheckboxComponent,
|
|
4352
3897
|
InputSearchComponent,
|
|
4353
3898
|
LinkActionComponent,
|
|
4354
3899
|
OverlayComponent,
|
|
4355
|
-
],
|
|
3900
|
+
], template: "<lib-overlay\n [position]=\"position()\"\n [disabled]=\"disabled()\"\n (opened)=\"onOverlayOpened()\"\n (closed)=\"onOverlayClosed()\"\n [width]=\"width()\"\n>\n <!-- Search Input (if enabled and not multiselect) -->\n @if (showSearchBar()) {\n <div class=\"c-dropdown-select__search\">\n <lib-input-search\n [placeholder]=\"searchPlaceholder()\"\n [formControl]=\"searchControl\"\n size=\"lg\"\n [fullWidth]=\"true\"\n ></lib-input-search>\n </div>\n }\n\n <!-- Options List -->\n <div class=\"c-dropdown-select__options\">\n @for (group of filteredOptions(); track group.label) {\n <!-- Option Group -->\n <div class=\"c-dropdown-select__group\">\n <!-- Group Label -->\n @if (group.label) {\n <div class=\"c-dropdown-select__group-label\">\n {{ group.label }}\n </div>\n }\n <!-- Group Options -->\n @for (option of group.options; track option.value) { @if (isMultiSelect())\n {\n <!-- For multiselect: use full checkbox component with label -->\n <div\n class=\"c-dropdown-select__option c-dropdown-select__option--multiselect\"\n >\n <lib-checkbox\n [label]=\"option.label\"\n [name]=\"'option-' + option.value\"\n [value]=\"option.value\"\n [ariaLabel]=\"'Select ' + option.label\"\n [state]=\"isSelected(option) ? 'checked' : 'unchecked'\"\n [disabled]=\"option.disabled || false\"\n (changed)=\"onCheckboxChange(option, $event.checked)\"\n ></lib-checkbox>\n </div>\n } @else {\n <!-- For single select: use button with icon and label -->\n <button\n type=\"button\"\n class=\"c-dropdown-select__option\"\n [class.c-dropdown-select__option--selected]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n >\n @if (option.icon) {\n <lib-icon\n [name]=\"option.icon\"\n size=\"lg\"\n class=\"c-dropdown-select__option-icon\"\n >\n </lib-icon>\n }\n\n <span class=\"c-dropdown-select__option-label\">\n {{ option.label }}\n </span>\n\n <!-- Check icon for selected option in single select -->\n @if (isSelected(option)) {\n <lib-icon\n name=\"checkcircle-fill\"\n size=\"lg\"\n color=\"action-primary-selected-background-default\"\n >\n </lib-icon>\n }\n </button>\n } }\n </div>\n }\n </div>\n\n <!-- Mobile Action Buttons in footer slot -->\n @if (showMobileActions()) {\n <div slot=\"footer\" class=\"c-dropdown-select__mobile-actions\">\n <lib-link-action\n [text]=\"cancelButtonText()\"\n variant=\"neutral\"\n size=\"md\"\n weight=\"bold\"\n (clicked)=\"onCancelClick()\"\n ></lib-link-action>\n <lib-link-action\n [text]=\"acceptButtonText()\"\n variant=\"primary\"\n size=\"md\"\n weight=\"bold\"\n (clicked)=\"onAcceptClick()\"\n ></lib-link-action>\n </div>\n }\n</lib-overlay>\n", styles: [".c-dropdown-select__search{padding:var(--space-container-padding-sm);border-bottom:var(--size-border-width-sm) solid var(--color-core-border-soft)}.c-dropdown-select__options{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0}.c-dropdown-select__group-label{color:var(--color-core-content-soft);padding:var(--space-component-padding-md) var(--space-component-padding-lg);font-family:var(--typography-label-sm-strong-family),sans-serif;font-weight:var(--typography-label-sm-strong-weight);font-size:var(--typography-label-sm-strong-size);letter-spacing:var(--typography-label-sm-strong-letter-spacing)}.c-dropdown-select__option{display:flex;align-items:center;width:100%;min-height:40px;gap:var(--space-component-gap-sm);padding:var(--space-component-padding-sm) var(--space-component-padding-lg);border:none;background:var(--color-action-neutral-background-default);color:var(--color-core-content-default);font-family:var(--typography-label-sm-default-family),sans-serif;font-weight:var(--typography-label-sm-default-weight);font-size:var(--typography-label-sm-default-size);letter-spacing:var(--typography-label-sm-default-letter-spacing);text-align:left;cursor:pointer}.c-dropdown-select__option:hover:not(:disabled){background-color:var(--color-action-neutral-background-hover)}.c-dropdown-select__option:focus-visible:not(:disabled){box-shadow:var(--focus-inset);outline:none}.c-dropdown-select__option:disabled{cursor:not-allowed;opacity:.5}.c-dropdown-select__option--multiselect{cursor:default;padding:0 var(--space-component-padding-lg)}.c-dropdown-select__option--multiselect:hover{background-color:var(--color-action-neutral-background-hover)}.c-dropdown-select__option--multiselect__option{font-family:var(--typography-label-md-default-family),sans-serif;font-weight:var(--typography-label-md-default-weight);font-size:var(--typography-label-md-default-size);letter-spacing:var(--typography-label-md-default-letter-spacing)}.c-dropdown-select__option-icon{flex-shrink:0;margin-right:12px;color:var(--color-core-content-soft)}.c-dropdown-select__option-label{flex:1;min-width:0}.c-dropdown-select__mobile-actions{display:flex;justify-content:space-between;align-items:center;width:100%}\n"] }]
|
|
4356
3901
|
}], ctorParameters: () => [], propDecorators: { overlay: [{
|
|
4357
3902
|
type: ViewChild,
|
|
4358
3903
|
args: [OverlayComponent]
|