Skip to content

Commit

Permalink
Image banner resize directive
Browse files Browse the repository at this point in the history
  • Loading branch information
RasmusTraeholt committed Feb 24, 2025
1 parent f404d1c commit bca7de8
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Directive, ElementRef, inject, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { ResizeObserverService } from '@kirbydesign/designsystem/shared';

/**
* @Description Temporary directive to ensure correct scroll position behavior on Safari.
*/
@Directive({
selector: `kirby-x-image-banner[kirbyImageBannerResize]`,
standalone: true,
})
export class ImageBannerResizeDirective implements OnInit, OnDestroy {
private host = inject(ElementRef);
private renderer = inject(Renderer2);
private resizeObserverService = inject(ResizeObserverService);

private readonly CONTAINER_BREAKPOINT = 600; // pixels
private readonly CONTAINER_MORE_THAN_CSS_CLASS_NAME = 'container-more-than-width';
private readonly CONTAINER_LESS_THAN_CSS_CLASS_NAME = 'container-less-than-width';

ngOnInit() {
this.resizeObserverService.observe(this.host.nativeElement, (entry) =>
this.handleHostResize(entry)
);
}

ngOnDestroy() {
this.resizeObserverService.unobserve(this.host.nativeElement);
}

private handleHostResize(entry: ResizeObserverEntry) {
if (entry.contentRect.width < this.CONTAINER_BREAKPOINT) {
this.renderer.removeClass(this.host.nativeElement, this.CONTAINER_MORE_THAN_CSS_CLASS_NAME);
this.renderer.addClass(this.host.nativeElement, this.CONTAINER_LESS_THAN_CSS_CLASS_NAME);
} else {
this.renderer.removeClass(this.host.nativeElement, this.CONTAINER_LESS_THAN_CSS_CLASS_NAME);
this.renderer.addClass(this.host.nativeElement, this.CONTAINER_MORE_THAN_CSS_CLASS_NAME);
}
}
}
109 changes: 95 additions & 14 deletions libs/extensions/angular/image-banner/src/image-banner.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@ $max-content-width: 324px;
$main-image-height-small: 132px;
$main-image-height-large: 164px;

:host(.none) {
.blur-image {
display: none;
}
// The breakpoint where container should switch from "narrow" to "wide"
$container-query-breakpoint: 600px;

.main-content-body-action-link {
color: var(--kirby-semi-dark);
@mixin container-less-than-width() {
@container banner (width < #{$container-query-breakpoint}) {
@content;
}
}

:host(.dark) .blur-image {
filter: blur($blur-radius) brightness(0.8);
@mixin container-more-than-width() {
@container banner (width >= #{$container-query-breakpoint}) {
@content;
}
}

:host(.light) .blur-image {
filter: blur($blur-radius) brightness(1.3);
// Elements with container-type: inline-size will collapse on Safari when navigating which prevents Angular from setting the correct scroll position on return.
// Until the issue is fixed on Safari we cannot use container queries. It's currently unknown whether this issue is being worked on or not.
:host(:not(.container-more-than-width, .container-less-than-width)) {
display: block;
container-name: banner;
container-type: inline-size;
}

// Temporary styling to support desired behaviour without container queries.
// See ImageBannerResizeDirective directive.
// START
:host(.container-less-than-width) {
.dismiss {
// on mobile we always want the button to be white as it sits on top of the image
Expand Down Expand Up @@ -79,6 +87,38 @@ $main-image-height-large: 164px;
}
}

// Temporary styling to support desired behaviour without container queries.
// See ImageBannerResizeDirective directive.
// END

:host(.none) {
.blur-image {
display: none;
}

.dismiss {
// on mobile we always want the button to be white as it sits on top of the image
@include container-less-than-width {
--kirby-inputs-background-color: var(--kirby-white);
--kirby-inputs-background-color-hover: var(--kirby-dark-overlay-10);
--kirby-inputs-background-color-active: var(--kirby-dark-overlay-20);
--kirby-inputs-color: var(--kirby-black);
}
}

.main-content-body-action-link {
color: var(--kirby-semi-dark);
}
}

:host(.dark) .blur-image {
filter: blur($blur-radius) brightness(0.8);
}

:host(.light) .blur-image {
filter: blur($blur-radius) brightness(1.3);
}

.blur-image-wrapper {
// We scale the image here with inset to avoid halo-bleed when using blur.
position: absolute;
Expand All @@ -94,22 +134,27 @@ $main-image-height-large: 164px;
object-position: center;
}

kirby-card {
height: 100%;
}

.main-content-wrapper {
width: 100%;
padding: utils.size('xxs');
box-sizing: border-box;
display: flex;
flex-direction: column;

@include container-more-than-width {
gap: utils.size('s');
flex-direction: initial;
}
}

.main-content-image-wrapper {
display: flex;
overflow: hidden;
border-radius: utils.border-radius('s');

@include container-more-than-width {
flex: 1;
}
}

.main-content {
Expand All @@ -126,6 +171,10 @@ kirby-card {

.main-content-header {
padding-inline-end: utils.size('xxs');

@include container-more-than-width {
padding-inline-end: utils.size('xl');
}
}

&:has(.main-content-body-action-link) {
Expand All @@ -141,6 +190,12 @@ kirby-card {
&:has(.body-text:empty):has(.header-text:empty) {
padding-block-end: 0;
}

@include container-more-than-width {
flex: 1;
gap: utils.size('xs');
padding: utils.size('xxs') utils.size('xxs') utils.size('xxs') 0;
}
}

.main-content-anchor {
Expand All @@ -162,24 +217,50 @@ kirby-card {
height: $main-image-height-small;
object-fit: cover;
object-position: center;

@include container-more-than-width {
height: $main-image-height-large;
}
}

.main-content-body {
display: flex;
justify-content: space-between;
gap: utils.size('s');
height: 100%;

@include container-more-than-width {
flex-direction: column;
max-width: $max-content-width;
}

.main-content-body-action-link {
@include container-more-than-width {
display: none;
}
}
}

.main-content-body-text {
display: block;
overflow: hidden;
max-height: utils.size('xl');
padding-inline-end: utils.size('xxs');

@include container-more-than-width {
padding-inline-end: utils.size('xxl');
max-height: utils.size('xxxxl');
}
}

.main-content-body-action-text {
display: none;

@include container-more-than-width {
align-self: start;
display: inline-flex;
margin: 0; // buttons have xxxs margin so reset that
}
}

.dismiss {
Expand Down
44 changes: 3 additions & 41 deletions libs/extensions/angular/image-banner/src/image-banner.component.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,19 @@
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
HostBinding,
inject,
Input,
OnDestroy,
Output,
Renderer2,
} from '@angular/core';
import { Component, EventEmitter, HostBinding, inject, Input, Output } from '@angular/core';
import { CardModule } from '@kirbydesign/designsystem/card';
import { ButtonComponent } from '@kirbydesign/designsystem/button';
import { IconModule } from '@kirbydesign/designsystem/icon';
import { ResizeObserverService, TranslationService } from '@kirbydesign/designsystem/shared';
import { TranslationService } from '@kirbydesign/designsystem/shared';

@Component({
selector: 'kirby-x-image-banner',
imports: [CardModule, ButtonComponent, IconModule, CommonModule],
templateUrl: './image-banner.component.html',
styleUrl: './image-banner.component.scss',
})
export class ImageBannerComponent implements AfterViewInit, OnDestroy {
private host = inject(ElementRef);
private renderer = inject(Renderer2);
private resizeObserverService = inject(ResizeObserverService);

export class ImageBannerComponent {
public translations = inject(TranslationService);

private readonly CONTAINER_BREAKPOINT = 600;

/**
* The title placed inside the image banners header.
*/
Expand Down Expand Up @@ -79,27 +62,6 @@ export class ImageBannerComponent implements AfterViewInit, OnDestroy {
@Output()
imageError = new EventEmitter<ErrorEvent>();

ngAfterViewInit() {
this.resizeObserverService.observe(this.host.nativeElement, (entry) =>
this.handleHostResize(entry)
);
}

ngOnDestroy() {
this.resizeObserverService.unobserve(this.host.nativeElement);
}

private handleHostResize(entry: ResizeObserverEntry) {
console.log(entry);
if (entry.contentRect.width < this.CONTAINER_BREAKPOINT) {
this.renderer.removeClass(this.host.nativeElement, 'container-more-than-width');
this.renderer.addClass(this.host.nativeElement, 'container-less-than-width');
} else {
this.renderer.removeClass(this.host.nativeElement, 'container-less-than-width');
this.renderer.addClass(this.host.nativeElement, 'container-more-than-width');
}
}

public bannerClicked(event: Event) {
const eventTarget = event.target as HTMLElement;
const dismissButtonClicked = eventTarget.closest('.dismiss');
Expand Down

0 comments on commit bca7de8

Please sign in to comment.