@crowdfarming/oliva-ds 1.84.0 → 1.85.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.
@@ -5644,6 +5644,7 @@ class ModalComponent {
5644
5644
  _isContentScrollableSignal = signal(false);
5645
5645
  _safeAreaInsetBottomSignal = signal(true);
5646
5646
  _autoFocusSignal = signal(true);
5647
+ _hasHeaderSlotSignal = signal(false);
5647
5648
  modalContentRef = viewChild('modalContent');
5648
5649
  bodyContentRef = viewChild('bodyContent');
5649
5650
  constructor() {
@@ -5696,6 +5697,12 @@ class ModalComponent {
5696
5697
  getAutoFocus() {
5697
5698
  return this._autoFocusSignal();
5698
5699
  }
5700
+ hasHeaderSlot() {
5701
+ return this._hasHeaderSlotSignal();
5702
+ }
5703
+ setHasHeaderSlot(hasHeaderSlot) {
5704
+ this._hasHeaderSlotSignal.set(hasHeaderSlot);
5705
+ }
5699
5706
  setOpen(open) {
5700
5707
  this._isOpenSignal.set(open);
5701
5708
  }
@@ -5837,6 +5844,9 @@ class ModalComponent {
5837
5844
  ];
5838
5845
  return c.filter(Boolean).join(' ');
5839
5846
  }
5847
+ shouldRenderHeader() {
5848
+ return (this.hasHeaderSlot() || !!this.getTitle() || this.getShowCloseButton());
5849
+ }
5840
5850
  getContentBodyContentClasses() {
5841
5851
  const c = [
5842
5852
  'c-modal__content__body__content',
@@ -5847,11 +5857,11 @@ class ModalComponent {
5847
5857
  return c.filter(Boolean).join(' ');
5848
5858
  }
5849
5859
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5850
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: ModalComponent, isStandalone: true, selector: "lib-modal", viewQueries: [{ propertyName: "modalContentRef", first: true, predicate: ["modalContent"], descendants: true, isSignal: true }, { propertyName: "bodyContentRef", first: true, predicate: ["bodyContent"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (isModalOpen()) {\n<div class=\"c-modal\" [ngClass]=\"getClasses()\">\n <!-- Backdrop -->\n <div\n class=\"c-modal__backdrop\"\n [ngClass]=\"getBackdropClasses()\"\n (click)=\"onBackdropClick()\"\n (keydown.escape)=\"onEscapeKey()\"\n tabindex=\"0\"\n role=\"button\"\n aria-label=\"Close modal\"\n ></div>\n\n <!-- Modal Content -->\n <div\n #modalContent\n class=\"c-modal__content\"\n [ngClass]=\"getContentClasses()\"\n (keydown.escape)=\"onEscapeKey()\"\n role=\"dialog\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"getTitle() ? 'modal-title' : null\"\n [attr.aria-describedby]=\"getDescription() ? 'modal-description' : null\"\n >\n <!-- Header -->\n @if (getTitle() || getShowCloseButton()) {\n <div [ngClass]=\"getHeaderClasses()\">\n <div class=\"c-modal__content__header__left\">\n @if (getTitle()) {\n <p id=\"modal-title\" class=\"c-modal__content__header__title\">\n {{ getTitle() }}\n </p>\n }\n </div>\n @if (getShowCloseButton()) {\n <div class=\"c-modal__content__header__right\">\n <lib-button-icon\n iconName=\"x-regular\"\n size=\"sm\"\n variant=\"neutral\"\n (click)=\"onCloseButtonClick()\"\n aria-label=\"Close modal\"\n ></lib-button-icon>\n </div>\n }\n </div>\n }\n\n <!-- Description -->\n <div class=\"c-modal__content__body\">\n <!-- Body Content (scrollable area) -->\n <div #bodyContent [ngClass]=\"getContentBodyContentClasses()\">\n @if (getDescription()) {\n <div id=\"modal-description\" class=\"c-modal__content__body__description\">\n <p>{{ getDescription() }}</p>\n </div>\n }\n <ng-content></ng-content>\n </div>\n </div>\n\n <!-- Footer with Buttons -->\n @if (getPrimaryButton() || getSecondaryButton()) {\n <div [ngClass]=\"getFooterClasses()\">\n @if (getSecondaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button secondary\"\n [text]=\"getSecondaryButton()!.text\"\n [variant]=\"getSecondaryButton()!.variant\"\n [disabled]=\"getSecondaryButton()!.disabled ?? false\"\n [loading]=\"getSecondaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onSecondaryButtonClick()\"\n ></lib-button>\n } @if (getPrimaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button primary\"\n [text]=\"getPrimaryButton()!.text\"\n [variant]=\"getPrimaryButton()!.variant\"\n [disabled]=\"getPrimaryButton()!.disabled ?? false\"\n [loading]=\"getPrimaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onPrimaryButtonClick()\"\n ></lib-button>\n }\n </div>\n }\n </div>\n</div>\n}\n", styles: [".c-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1000;display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out}.c-modal--open{opacity:1;visibility:visible}.c-modal__content{background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) 0;box-shadow:var(--elevation-floating);border-radius:var(--size-border-radius-lg);overflow:hidden;display:flex;flex-direction:column;width:100%;box-sizing:border-box;max-height:min(812px,100vh - 128px)}.c-modal__content--open{transform:scale(1)}.c-modal__content--xs{max-width:360px}.c-modal__content--sm{max-width:416px}.c-modal__content--md{max-width:636px}.c-modal__content--lg{max-width:856px}.c-modal__content--safe-area-inset-bottom{padding-bottom:calc(env(safe-area-inset-bottom,0) + var(--space-container-padding-sm))}.c-modal__content__header{display:flex;align-items:center;background-color:var(--color-core-background-surface-floating);padding:0 var(--space-container-padding-sm) var(--space-container-padding-sm);gap:var(--space-container-gap-sm);border-bottom:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__header--scrollable{z-index:1;border-bottom-color:var(--color-core-border-soft)}.c-modal__content__header__right{flex:1;display:flex;justify-content:flex-end}.c-modal__content__header__title{margin:0;font-family:var(--typography-label-lg-strong-family),sans-serif;font-weight:var(--typography-label-lg-strong-weight);font-size:var(--typography-label-lg-strong-size);line-height:var(--typography-label-lg-strong-line-height);letter-spacing:var(--typography-label-lg-strong-letter-spacing);color:var(--color-core-content-default)}.c-modal__content__body{display:flex;flex-direction:column;flex:1;gap:var(--space-container-gap-md);overflow:hidden;min-height:0}.c-modal__content__body__description p{margin:0;color:var(--color-core-content-soft);font-family:var(--typography-body-md-family),sans-serif;font-weight:var(--typography-body-md-weight);font-size:var(--typography-body-md-size);line-height:var(--typography-body-md-line-height);letter-spacing:var(--typography-body-md-letter-spacing)}.c-modal__content__body__content{padding:0 var(--space-container-padding-sm);display:flex;flex-direction:column;gap:var(--space-container-gap-md);overflow-y:auto;flex:1;min-height:0}.c-modal__content__body__content--scrollable{padding:var(--space-container-padding-sm)}.c-modal__content__footer{display:flex;justify-content:flex-end;gap:var(--space-component-gap-md);background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) var(--space-container-padding-sm) 0;border-top:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__footer--scrollable{z-index:1;border-top-color:var(--color-core-border-soft)}@media (max-width: 767px){.c-modal__content__footer{flex-direction:column;gap:var(--space-component-gap-md)}}@media (max-width: 767px){.c-modal__content__footer__button{width:100%}.c-modal__content__footer__button.primary{order:1}.c-modal__content__footer__button.secondary{order:2}}.c-modal__backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:var(--color-effect-overlay);opacity:0;transition:opacity .2s ease-in-out}.c-modal__backdrop--open{opacity:1}@media (max-width: 767px){.c-modal{align-items:flex-end}.c-modal--xs .c-modal__content,.c-modal--sm .c-modal__content,.c-modal--md .c-modal__content{position:fixed;bottom:0;left:0;right:0;width:100%;max-width:none;max-height:100vh;transform:translateY(100%);transition:transform .3s ease-in-out;border-bottom-left-radius:0;border-bottom-right-radius:0}.c-modal--xs.c-modal--open .c-modal__content,.c-modal--sm.c-modal--open .c-modal__content,.c-modal--md.c-modal--open .c-modal__content{transform:translateY(0);perspective:none;contain:none}.c-modal--lg{align-items:stretch}.c-modal--lg .c-modal__content{position:fixed;inset:0;width:100%;height:100%;max-height:100vh;max-width:none;border-radius:0;transform:scale(.95);transition:transform .3s ease-in-out}.c-modal--lg.c-modal--open .c-modal__content{transform:scale(1)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["text", "ariaLabel", "disabled", "loading", "skeletonActive", "variant", "size", "loadingText", "fullWidth", "iconBefore", "iconAfter", "type"], outputs: ["clicked"] }, { kind: "component", type: ButtonIconComponent, selector: "lib-button-icon", inputs: ["ariaLabel", "variant", "size", "iconName", "disabled", "loading", "skeletonActive"], outputs: ["clicked"] }] });
5860
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.7", type: ModalComponent, isStandalone: true, selector: "lib-modal", viewQueries: [{ propertyName: "modalContentRef", first: true, predicate: ["modalContent"], descendants: true, isSignal: true }, { propertyName: "bodyContentRef", first: true, predicate: ["bodyContent"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (isModalOpen()) {\n<div class=\"c-modal\" [ngClass]=\"getClasses()\">\n <!-- Backdrop -->\n <div\n class=\"c-modal__backdrop\"\n [ngClass]=\"getBackdropClasses()\"\n (click)=\"onBackdropClick()\"\n (keydown.escape)=\"onEscapeKey()\"\n tabindex=\"0\"\n role=\"button\"\n aria-label=\"Close modal\"\n ></div>\n\n <!-- Modal Content -->\n <div\n #modalContent\n class=\"c-modal__content\"\n [ngClass]=\"getContentClasses()\"\n (keydown.escape)=\"onEscapeKey()\"\n role=\"dialog\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"getTitle() && !hasHeaderSlot() ? 'modal-title' : null\"\n [attr.aria-describedby]=\"getDescription() ? 'modal-description' : null\"\n >\n <!-- Header -->\n @if (shouldRenderHeader()) {\n <div [ngClass]=\"getHeaderClasses()\">\n <div class=\"c-modal__content__header__left\">\n @if (hasHeaderSlot()) {\n <div class=\"c-modal__content__header__slot\"></div>\n } @else if (getTitle()) {\n <p id=\"modal-title\" class=\"c-modal__content__header__title\">\n {{ getTitle() }}\n </p>\n }\n </div>\n @if (getShowCloseButton()) {\n <div class=\"c-modal__content__header__right\">\n <lib-button-icon\n iconName=\"x-regular\"\n size=\"sm\"\n variant=\"neutral\"\n (click)=\"onCloseButtonClick()\"\n aria-label=\"Close modal\"\n ></lib-button-icon>\n </div>\n }\n </div>\n }\n\n <!-- Description -->\n <div class=\"c-modal__content__body\">\n <!-- Body Content (scrollable area) -->\n <div #bodyContent [ngClass]=\"getContentBodyContentClasses()\">\n @if (getDescription()) {\n <div id=\"modal-description\" class=\"c-modal__content__body__description\">\n <p>{{ getDescription() }}</p>\n </div>\n }\n <ng-content></ng-content>\n </div>\n </div>\n\n <!-- Footer with Buttons -->\n @if (getPrimaryButton() || getSecondaryButton()) {\n <div [ngClass]=\"getFooterClasses()\">\n @if (getSecondaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button secondary\"\n [text]=\"getSecondaryButton()!.text\"\n [variant]=\"getSecondaryButton()!.variant\"\n [disabled]=\"getSecondaryButton()!.disabled ?? false\"\n [loading]=\"getSecondaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onSecondaryButtonClick()\"\n ></lib-button>\n } @if (getPrimaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button primary\"\n [text]=\"getPrimaryButton()!.text\"\n [variant]=\"getPrimaryButton()!.variant\"\n [disabled]=\"getPrimaryButton()!.disabled ?? false\"\n [loading]=\"getPrimaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onPrimaryButtonClick()\"\n ></lib-button>\n }\n </div>\n }\n </div>\n</div>\n}\n", styles: [".c-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1000;display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out}.c-modal--open{opacity:1;visibility:visible}.c-modal__content{background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) 0;box-shadow:var(--elevation-floating);border-radius:var(--size-border-radius-lg);overflow:hidden;display:flex;flex-direction:column;width:100%;box-sizing:border-box;max-height:min(812px,100vh - 128px)}.c-modal__content--open{transform:scale(1)}.c-modal__content--xs{max-width:360px}.c-modal__content--sm{max-width:416px}.c-modal__content--md{max-width:636px}.c-modal__content--lg{max-width:856px}.c-modal__content--safe-area-inset-bottom{padding-bottom:calc(env(safe-area-inset-bottom,0) + var(--space-container-padding-sm))}.c-modal__content__header{display:flex;align-items:flex-start;background-color:var(--color-core-background-surface-floating);padding:0 var(--space-container-padding-sm) var(--space-container-padding-sm);gap:var(--space-container-gap-sm);border-bottom:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__header--scrollable{z-index:1;border-bottom-color:var(--color-core-border-soft)}.c-modal__content__header__left{flex:1;min-width:0;display:flex;align-items:center}.c-modal__content__header__right{flex:1;display:flex;justify-content:flex-end}.c-modal__content__header__slot{flex:1;min-width:0;display:flex;align-items:center}.c-modal__content__header__title{margin:0;font-family:var(--typography-label-lg-strong-family),sans-serif;font-weight:var(--typography-label-lg-strong-weight);font-size:var(--typography-label-lg-strong-size);line-height:var(--typography-label-lg-strong-line-height);letter-spacing:var(--typography-label-lg-strong-letter-spacing);color:var(--color-core-content-default)}.c-modal__content__body{display:flex;flex-direction:column;flex:1;gap:var(--space-container-gap-md);overflow:hidden;min-height:0}.c-modal__content__body__description p{margin:0;color:var(--color-core-content-soft);font-family:var(--typography-body-md-family),sans-serif;font-weight:var(--typography-body-md-weight);font-size:var(--typography-body-md-size);line-height:var(--typography-body-md-line-height);letter-spacing:var(--typography-body-md-letter-spacing)}.c-modal__content__body__content{padding:0 var(--space-container-padding-sm);display:flex;flex-direction:column;gap:var(--space-container-gap-md);overflow-y:auto;flex:1;min-height:0}.c-modal__content__body__content--scrollable{padding:var(--space-container-padding-sm)}.c-modal__content__footer{display:flex;justify-content:flex-end;gap:var(--space-component-gap-md);background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) var(--space-container-padding-sm) 0;border-top:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__footer--scrollable{z-index:1;border-top-color:var(--color-core-border-soft)}@media (max-width: 767px){.c-modal__content__footer{flex-direction:column;gap:var(--space-component-gap-md)}}@media (max-width: 767px){.c-modal__content__footer__button{width:100%}.c-modal__content__footer__button.primary{order:1}.c-modal__content__footer__button.secondary{order:2}}.c-modal__backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:var(--color-effect-overlay);opacity:0;transition:opacity .2s ease-in-out}.c-modal__backdrop--open{opacity:1}@media (max-width: 767px){.c-modal{align-items:flex-end}.c-modal--xs .c-modal__content,.c-modal--sm .c-modal__content,.c-modal--md .c-modal__content{position:fixed;bottom:0;left:0;right:0;width:100%;max-width:none;max-height:100vh;transform:translateY(100%);transition:transform .3s ease-in-out;border-bottom-left-radius:0;border-bottom-right-radius:0}.c-modal--xs.c-modal--open .c-modal__content,.c-modal--sm.c-modal--open .c-modal__content,.c-modal--md.c-modal--open .c-modal__content{transform:translateY(0);perspective:none;contain:none}.c-modal--lg{align-items:stretch}.c-modal--lg .c-modal__content{position:fixed;inset:0;width:100%;height:100%;max-height:100vh;max-width:none;border-radius:0;transform:scale(.95);transition:transform .3s ease-in-out}.c-modal--lg.c-modal--open .c-modal__content{transform:scale(1)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["text", "ariaLabel", "disabled", "loading", "skeletonActive", "variant", "size", "loadingText", "fullWidth", "iconBefore", "iconAfter", "type"], outputs: ["clicked"] }, { kind: "component", type: ButtonIconComponent, selector: "lib-button-icon", inputs: ["ariaLabel", "variant", "size", "iconName", "disabled", "loading", "skeletonActive"], outputs: ["clicked"] }] });
5851
5861
  }
5852
5862
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.7", ngImport: i0, type: ModalComponent, decorators: [{
5853
5863
  type: Component,
5854
- args: [{ selector: 'lib-modal', imports: [NgClass, ButtonComponent, ButtonIconComponent], template: "@if (isModalOpen()) {\n<div class=\"c-modal\" [ngClass]=\"getClasses()\">\n <!-- Backdrop -->\n <div\n class=\"c-modal__backdrop\"\n [ngClass]=\"getBackdropClasses()\"\n (click)=\"onBackdropClick()\"\n (keydown.escape)=\"onEscapeKey()\"\n tabindex=\"0\"\n role=\"button\"\n aria-label=\"Close modal\"\n ></div>\n\n <!-- Modal Content -->\n <div\n #modalContent\n class=\"c-modal__content\"\n [ngClass]=\"getContentClasses()\"\n (keydown.escape)=\"onEscapeKey()\"\n role=\"dialog\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"getTitle() ? 'modal-title' : null\"\n [attr.aria-describedby]=\"getDescription() ? 'modal-description' : null\"\n >\n <!-- Header -->\n @if (getTitle() || getShowCloseButton()) {\n <div [ngClass]=\"getHeaderClasses()\">\n <div class=\"c-modal__content__header__left\">\n @if (getTitle()) {\n <p id=\"modal-title\" class=\"c-modal__content__header__title\">\n {{ getTitle() }}\n </p>\n }\n </div>\n @if (getShowCloseButton()) {\n <div class=\"c-modal__content__header__right\">\n <lib-button-icon\n iconName=\"x-regular\"\n size=\"sm\"\n variant=\"neutral\"\n (click)=\"onCloseButtonClick()\"\n aria-label=\"Close modal\"\n ></lib-button-icon>\n </div>\n }\n </div>\n }\n\n <!-- Description -->\n <div class=\"c-modal__content__body\">\n <!-- Body Content (scrollable area) -->\n <div #bodyContent [ngClass]=\"getContentBodyContentClasses()\">\n @if (getDescription()) {\n <div id=\"modal-description\" class=\"c-modal__content__body__description\">\n <p>{{ getDescription() }}</p>\n </div>\n }\n <ng-content></ng-content>\n </div>\n </div>\n\n <!-- Footer with Buttons -->\n @if (getPrimaryButton() || getSecondaryButton()) {\n <div [ngClass]=\"getFooterClasses()\">\n @if (getSecondaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button secondary\"\n [text]=\"getSecondaryButton()!.text\"\n [variant]=\"getSecondaryButton()!.variant\"\n [disabled]=\"getSecondaryButton()!.disabled ?? false\"\n [loading]=\"getSecondaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onSecondaryButtonClick()\"\n ></lib-button>\n } @if (getPrimaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button primary\"\n [text]=\"getPrimaryButton()!.text\"\n [variant]=\"getPrimaryButton()!.variant\"\n [disabled]=\"getPrimaryButton()!.disabled ?? false\"\n [loading]=\"getPrimaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onPrimaryButtonClick()\"\n ></lib-button>\n }\n </div>\n }\n </div>\n</div>\n}\n", styles: [".c-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1000;display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out}.c-modal--open{opacity:1;visibility:visible}.c-modal__content{background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) 0;box-shadow:var(--elevation-floating);border-radius:var(--size-border-radius-lg);overflow:hidden;display:flex;flex-direction:column;width:100%;box-sizing:border-box;max-height:min(812px,100vh - 128px)}.c-modal__content--open{transform:scale(1)}.c-modal__content--xs{max-width:360px}.c-modal__content--sm{max-width:416px}.c-modal__content--md{max-width:636px}.c-modal__content--lg{max-width:856px}.c-modal__content--safe-area-inset-bottom{padding-bottom:calc(env(safe-area-inset-bottom,0) + var(--space-container-padding-sm))}.c-modal__content__header{display:flex;align-items:center;background-color:var(--color-core-background-surface-floating);padding:0 var(--space-container-padding-sm) var(--space-container-padding-sm);gap:var(--space-container-gap-sm);border-bottom:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__header--scrollable{z-index:1;border-bottom-color:var(--color-core-border-soft)}.c-modal__content__header__right{flex:1;display:flex;justify-content:flex-end}.c-modal__content__header__title{margin:0;font-family:var(--typography-label-lg-strong-family),sans-serif;font-weight:var(--typography-label-lg-strong-weight);font-size:var(--typography-label-lg-strong-size);line-height:var(--typography-label-lg-strong-line-height);letter-spacing:var(--typography-label-lg-strong-letter-spacing);color:var(--color-core-content-default)}.c-modal__content__body{display:flex;flex-direction:column;flex:1;gap:var(--space-container-gap-md);overflow:hidden;min-height:0}.c-modal__content__body__description p{margin:0;color:var(--color-core-content-soft);font-family:var(--typography-body-md-family),sans-serif;font-weight:var(--typography-body-md-weight);font-size:var(--typography-body-md-size);line-height:var(--typography-body-md-line-height);letter-spacing:var(--typography-body-md-letter-spacing)}.c-modal__content__body__content{padding:0 var(--space-container-padding-sm);display:flex;flex-direction:column;gap:var(--space-container-gap-md);overflow-y:auto;flex:1;min-height:0}.c-modal__content__body__content--scrollable{padding:var(--space-container-padding-sm)}.c-modal__content__footer{display:flex;justify-content:flex-end;gap:var(--space-component-gap-md);background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) var(--space-container-padding-sm) 0;border-top:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__footer--scrollable{z-index:1;border-top-color:var(--color-core-border-soft)}@media (max-width: 767px){.c-modal__content__footer{flex-direction:column;gap:var(--space-component-gap-md)}}@media (max-width: 767px){.c-modal__content__footer__button{width:100%}.c-modal__content__footer__button.primary{order:1}.c-modal__content__footer__button.secondary{order:2}}.c-modal__backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:var(--color-effect-overlay);opacity:0;transition:opacity .2s ease-in-out}.c-modal__backdrop--open{opacity:1}@media (max-width: 767px){.c-modal{align-items:flex-end}.c-modal--xs .c-modal__content,.c-modal--sm .c-modal__content,.c-modal--md .c-modal__content{position:fixed;bottom:0;left:0;right:0;width:100%;max-width:none;max-height:100vh;transform:translateY(100%);transition:transform .3s ease-in-out;border-bottom-left-radius:0;border-bottom-right-radius:0}.c-modal--xs.c-modal--open .c-modal__content,.c-modal--sm.c-modal--open .c-modal__content,.c-modal--md.c-modal--open .c-modal__content{transform:translateY(0);perspective:none;contain:none}.c-modal--lg{align-items:stretch}.c-modal--lg .c-modal__content{position:fixed;inset:0;width:100%;height:100%;max-height:100vh;max-width:none;border-radius:0;transform:scale(.95);transition:transform .3s ease-in-out}.c-modal--lg.c-modal--open .c-modal__content{transform:scale(1)}}\n"] }]
5864
+ args: [{ selector: 'lib-modal', imports: [NgClass, ButtonComponent, ButtonIconComponent], template: "@if (isModalOpen()) {\n<div class=\"c-modal\" [ngClass]=\"getClasses()\">\n <!-- Backdrop -->\n <div\n class=\"c-modal__backdrop\"\n [ngClass]=\"getBackdropClasses()\"\n (click)=\"onBackdropClick()\"\n (keydown.escape)=\"onEscapeKey()\"\n tabindex=\"0\"\n role=\"button\"\n aria-label=\"Close modal\"\n ></div>\n\n <!-- Modal Content -->\n <div\n #modalContent\n class=\"c-modal__content\"\n [ngClass]=\"getContentClasses()\"\n (keydown.escape)=\"onEscapeKey()\"\n role=\"dialog\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"getTitle() && !hasHeaderSlot() ? 'modal-title' : null\"\n [attr.aria-describedby]=\"getDescription() ? 'modal-description' : null\"\n >\n <!-- Header -->\n @if (shouldRenderHeader()) {\n <div [ngClass]=\"getHeaderClasses()\">\n <div class=\"c-modal__content__header__left\">\n @if (hasHeaderSlot()) {\n <div class=\"c-modal__content__header__slot\"></div>\n } @else if (getTitle()) {\n <p id=\"modal-title\" class=\"c-modal__content__header__title\">\n {{ getTitle() }}\n </p>\n }\n </div>\n @if (getShowCloseButton()) {\n <div class=\"c-modal__content__header__right\">\n <lib-button-icon\n iconName=\"x-regular\"\n size=\"sm\"\n variant=\"neutral\"\n (click)=\"onCloseButtonClick()\"\n aria-label=\"Close modal\"\n ></lib-button-icon>\n </div>\n }\n </div>\n }\n\n <!-- Description -->\n <div class=\"c-modal__content__body\">\n <!-- Body Content (scrollable area) -->\n <div #bodyContent [ngClass]=\"getContentBodyContentClasses()\">\n @if (getDescription()) {\n <div id=\"modal-description\" class=\"c-modal__content__body__description\">\n <p>{{ getDescription() }}</p>\n </div>\n }\n <ng-content></ng-content>\n </div>\n </div>\n\n <!-- Footer with Buttons -->\n @if (getPrimaryButton() || getSecondaryButton()) {\n <div [ngClass]=\"getFooterClasses()\">\n @if (getSecondaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button secondary\"\n [text]=\"getSecondaryButton()!.text\"\n [variant]=\"getSecondaryButton()!.variant\"\n [disabled]=\"getSecondaryButton()!.disabled ?? false\"\n [loading]=\"getSecondaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onSecondaryButtonClick()\"\n ></lib-button>\n } @if (getPrimaryButton()) {\n <lib-button\n class=\"c-modal__content__footer__button primary\"\n [text]=\"getPrimaryButton()!.text\"\n [variant]=\"getPrimaryButton()!.variant\"\n [disabled]=\"getPrimaryButton()!.disabled ?? false\"\n [loading]=\"getPrimaryButton()!.loading ?? false\"\n [fullWidth]=\"true\"\n (clicked)=\"onPrimaryButtonClick()\"\n ></lib-button>\n }\n </div>\n }\n </div>\n</div>\n}\n", styles: [".c-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1000;display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out}.c-modal--open{opacity:1;visibility:visible}.c-modal__content{background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) 0;box-shadow:var(--elevation-floating);border-radius:var(--size-border-radius-lg);overflow:hidden;display:flex;flex-direction:column;width:100%;box-sizing:border-box;max-height:min(812px,100vh - 128px)}.c-modal__content--open{transform:scale(1)}.c-modal__content--xs{max-width:360px}.c-modal__content--sm{max-width:416px}.c-modal__content--md{max-width:636px}.c-modal__content--lg{max-width:856px}.c-modal__content--safe-area-inset-bottom{padding-bottom:calc(env(safe-area-inset-bottom,0) + var(--space-container-padding-sm))}.c-modal__content__header{display:flex;align-items:flex-start;background-color:var(--color-core-background-surface-floating);padding:0 var(--space-container-padding-sm) var(--space-container-padding-sm);gap:var(--space-container-gap-sm);border-bottom:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__header--scrollable{z-index:1;border-bottom-color:var(--color-core-border-soft)}.c-modal__content__header__left{flex:1;min-width:0;display:flex;align-items:center}.c-modal__content__header__right{flex:1;display:flex;justify-content:flex-end}.c-modal__content__header__slot{flex:1;min-width:0;display:flex;align-items:center}.c-modal__content__header__title{margin:0;font-family:var(--typography-label-lg-strong-family),sans-serif;font-weight:var(--typography-label-lg-strong-weight);font-size:var(--typography-label-lg-strong-size);line-height:var(--typography-label-lg-strong-line-height);letter-spacing:var(--typography-label-lg-strong-letter-spacing);color:var(--color-core-content-default)}.c-modal__content__body{display:flex;flex-direction:column;flex:1;gap:var(--space-container-gap-md);overflow:hidden;min-height:0}.c-modal__content__body__description p{margin:0;color:var(--color-core-content-soft);font-family:var(--typography-body-md-family),sans-serif;font-weight:var(--typography-body-md-weight);font-size:var(--typography-body-md-size);line-height:var(--typography-body-md-line-height);letter-spacing:var(--typography-body-md-letter-spacing)}.c-modal__content__body__content{padding:0 var(--space-container-padding-sm);display:flex;flex-direction:column;gap:var(--space-container-gap-md);overflow-y:auto;flex:1;min-height:0}.c-modal__content__body__content--scrollable{padding:var(--space-container-padding-sm)}.c-modal__content__footer{display:flex;justify-content:flex-end;gap:var(--space-component-gap-md);background-color:var(--color-core-background-surface-floating);padding:var(--space-container-padding-sm) var(--space-container-padding-sm) 0;border-top:var(--size-border-width-sm) solid transparent;transition:border-color .15s ease-in-out}.c-modal__content__footer--scrollable{z-index:1;border-top-color:var(--color-core-border-soft)}@media (max-width: 767px){.c-modal__content__footer{flex-direction:column;gap:var(--space-component-gap-md)}}@media (max-width: 767px){.c-modal__content__footer__button{width:100%}.c-modal__content__footer__button.primary{order:1}.c-modal__content__footer__button.secondary{order:2}}.c-modal__backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:var(--color-effect-overlay);opacity:0;transition:opacity .2s ease-in-out}.c-modal__backdrop--open{opacity:1}@media (max-width: 767px){.c-modal{align-items:flex-end}.c-modal--xs .c-modal__content,.c-modal--sm .c-modal__content,.c-modal--md .c-modal__content{position:fixed;bottom:0;left:0;right:0;width:100%;max-width:none;max-height:100vh;transform:translateY(100%);transition:transform .3s ease-in-out;border-bottom-left-radius:0;border-bottom-right-radius:0}.c-modal--xs.c-modal--open .c-modal__content,.c-modal--sm.c-modal--open .c-modal__content,.c-modal--md.c-modal--open .c-modal__content{transform:translateY(0);perspective:none;contain:none}.c-modal--lg{align-items:stretch}.c-modal--lg .c-modal__content{position:fixed;inset:0;width:100%;height:100%;max-height:100vh;max-width:none;border-radius:0;transform:scale(.95);transition:transform .3s ease-in-out}.c-modal--lg.c-modal--open .c-modal__content{transform:scale(1)}}\n"] }]
5855
5865
  }], ctorParameters: () => [] });
5856
5866
 
5857
5867
  class ModalService {
@@ -5863,6 +5873,7 @@ class ModalService {
5863
5873
  openModals = new Map();
5864
5874
  constructor() {
5865
5875
  this.subscribeToNavigationEvents();
5876
+ this.ensureCdkOverlayStacksAboveModal();
5866
5877
  }
5867
5878
  subscribeToNavigationEvents() {
5868
5879
  if (!isPlatformBrowser(this.platformId)) {
@@ -5874,6 +5885,24 @@ class ModalService {
5874
5885
  this.closeAll();
5875
5886
  });
5876
5887
  }
5888
+ // The modal element is appended to <body> with z-index: 1000. Angular CDK's
5889
+ // overlay container is also a sibling of <body> with z-index: 1000, so when
5890
+ // a tooltip/select/etc. is opened inside a modal the modal ends up on top
5891
+ // because it sits later in document order. We raise the CDK container above
5892
+ // the modal so any CDK overlay (tooltip, popover, dropdown...) shows over it.
5893
+ ensureCdkOverlayStacksAboveModal() {
5894
+ if (!isPlatformBrowser(this.platformId)) {
5895
+ return;
5896
+ }
5897
+ const styleId = 'oliva-ds-modal-overlay-stacking';
5898
+ if (document.getElementById(styleId)) {
5899
+ return;
5900
+ }
5901
+ const style = document.createElement('style');
5902
+ style.id = styleId;
5903
+ style.textContent = '.cdk-overlay-container { z-index: 1100; }';
5904
+ document.head.appendChild(style);
5905
+ }
5877
5906
  closeAll({ force = false } = {}) {
5878
5907
  const modalsToClose = Array.from(this.openModals.values()).filter((entry) => force || !entry.persist);
5879
5908
  modalsToClose.forEach((entry) => {
@@ -5895,10 +5924,28 @@ class ModalService {
5895
5924
  if (config.data) {
5896
5925
  Object.assign(contentRef.instance, config.data);
5897
5926
  }
5927
+ // Create header component if provided
5928
+ const headerRef = config.headerComponent
5929
+ ? createComponent(config.headerComponent, {
5930
+ environmentInjector: this.appRef.injector,
5931
+ elementInjector: this.injector,
5932
+ })
5933
+ : undefined;
5934
+ if (headerRef && config.headerData) {
5935
+ // Use setInput so it works for both decorator-based inputs and the new
5936
+ // signal `input()` API (assigning directly would clobber InputSignals).
5937
+ for (const [key, value] of Object.entries(config.headerData)) {
5938
+ headerRef.setInput(key, value);
5939
+ }
5940
+ }
5898
5941
  this.appRef.attachView(modalRef.hostView);
5899
5942
  this.appRef.attachView(contentRef.hostView);
5943
+ if (headerRef) {
5944
+ this.appRef.attachView(headerRef.hostView);
5945
+ }
5900
5946
  document.body.appendChild(modalRef.location.nativeElement);
5901
5947
  modalRef.instance.setConfig(config);
5948
+ modalRef.instance.setHasHeaderSlot(!!headerRef);
5902
5949
  let result;
5903
5950
  let resolvePromise;
5904
5951
  const afterClosedPromise = new Promise((resolve) => {
@@ -5932,6 +5979,7 @@ class ModalService {
5932
5979
  const entry = {
5933
5980
  modalRef,
5934
5981
  contentRef,
5982
+ headerRef,
5935
5983
  resolvePromise: resolvePromise,
5936
5984
  result,
5937
5985
  modalRefResult,
@@ -6048,6 +6096,16 @@ class ModalService {
6048
6096
  console.error('No suitable content container found');
6049
6097
  }
6050
6098
  }
6099
+ if (headerRef) {
6100
+ const headerElement = headerRef.location.nativeElement;
6101
+ const headerSlotContainer = modalRef.location.nativeElement.querySelector('.c-modal__content__header__slot');
6102
+ if (headerSlotContainer) {
6103
+ headerSlotContainer.appendChild(headerElement);
6104
+ }
6105
+ else {
6106
+ console.error('Header slot container not found in modal');
6107
+ }
6108
+ }
6051
6109
  }, 10);
6052
6110
  return modalRefResult;
6053
6111
  }
@@ -6056,15 +6114,21 @@ class ModalService {
6056
6114
  if (!entry) {
6057
6115
  return;
6058
6116
  }
6059
- const { modalRef, contentRef, resolvePromise, result } = entry;
6117
+ const { modalRef, contentRef, headerRef, resolvePromise, result } = entry;
6060
6118
  modalRef.instance.setOpen(false);
6061
6119
  this.openModals.delete(modalRefResult);
6062
6120
  // Wait for animation to complete before destroying
6063
6121
  setTimeout(() => {
6064
6122
  this.appRef.detachView(modalRef.hostView);
6065
6123
  this.appRef.detachView(contentRef.hostView);
6124
+ if (headerRef) {
6125
+ this.appRef.detachView(headerRef.hostView);
6126
+ }
6066
6127
  modalRef.destroy();
6067
6128
  contentRef.destroy();
6129
+ if (headerRef) {
6130
+ headerRef.destroy();
6131
+ }
6068
6132
  resolvePromise(result);
6069
6133
  }, 200);
6070
6134
  }