Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { provideDaffDesignContainerExamplesContent } from '@daffodil/design-exam
import { provideDaffDesignFormFieldExamplesContent } from '@daffodil/design-examples/form-field';
import { provideDaffDesignHeroExamplesContent } from '@daffodil/design-examples/hero';
import { provideDaffDesignImageExamplesContent } from '@daffodil/design-examples/image';
import { provideDaffDesignImageLiteExamplesContent } from '@daffodil/design-examples/image-lite';
import { provideDaffDesignInputExamplesContent } from '@daffodil/design-examples/input';
import { provideDaffDesignListExamplesContent } from '@daffodil/design-examples/list';
import { provideDaffDesignMediaGalleryExamplesContent } from '@daffodil/design-examples/media-gallery';
Expand Down Expand Up @@ -57,6 +58,7 @@ export const provideDaffioDesignExamplesContent = () => makeEnvironmentProviders
provideDaffDesignFormFieldExamplesContent(),
provideDaffDesignHeroExamplesContent(),
provideDaffDesignImageExamplesContent(),
provideDaffDesignImageLiteExamplesContent(),
provideDaffDesignInputExamplesContent(),
provideDaffDesignListExamplesContent(),
provideDaffDesignMediaGalleryExamplesContent(),
Expand Down
7 changes: 7 additions & 0 deletions libs/design-examples/image-lite/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
"lib": {
"entryFile": "src/index.ts",
"styleIncludePaths": ["../../src/scss"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<img daff-image
ngSrc="https://assets.daff.io/ergonomic_bronze_pants.png"
alt="Ergonomic Bronze Pants T-Shirt"
width="300"
height="300" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
ChangeDetectionStrategy,
Component,
} from '@angular/core';

import { DAFF_IMAGE_LITE_COMPONENTS } from '@daffodil/design/image-lite';

@Component({
selector: 'basic-image-lite-example',
templateUrl: './basic-image-lite.component.html',
styleUrl: './basic-image-lite.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
DAFF_IMAGE_LITE_COMPONENTS,
],
})
export class BasicImageLiteExampleComponent {

}
1 change: 1 addition & 0 deletions libs/design-examples/image-lite/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public_api';
14 changes: 14 additions & 0 deletions libs/design-examples/image-lite/src/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { makeEnvironmentProviders } from '@angular/core';

import { provideDaffDocsExampleContent } from '@daffodil/docs';

export const provideDaffDesignImageLiteExamplesContent = () => makeEnvironmentProviders(provideDaffDocsExampleContent(
{
id: 'basic-image-lite',
component: () => import('./basic-image-lite/basic-image-lite.component').then(c => c.BasicImageLiteExampleComponent),
},
{
id: 'skeleton-image-lite',
component: () => import('./skeleton-image-lite/skeleton-image-lite.component').then(c => c.SkeletonImageLiteExampleComponent),
},
));
4 changes: 4 additions & 0 deletions libs/design-examples/image-lite/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { SkeletonImageLiteExampleComponent } from './skeleton-image-lite/skeleton-image-lite.component';
export { BasicImageLiteExampleComponent } from './basic-image-lite/basic-image-lite.component';

export { provideDaffDesignImageLiteExamplesContent } from './provider';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<img daff-image
[skeleton]="true"
ngSrc="https://assets.daff.io/ergonomic_bronze_pants.png"
alt="Ergonomic Bronze Pants T-Shirt"
width="300"
height="300" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
ChangeDetectionStrategy,
Component,
} from '@angular/core';

import { DAFF_IMAGE_LITE_COMPONENTS } from '@daffodil/design/image-lite';

@Component({
selector: 'skeleton-image-lite-example',
templateUrl: './skeleton-image-lite.component.html',
styleUrl: './skeleton-image-lite.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
DAFF_IMAGE_LITE_COMPONENTS,
],
})
export class SkeletonImageLiteExampleComponent {

}
3 changes: 3 additions & 0 deletions libs/design-examples/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"@daffodil/design-examples/image": [
"libs/design-examples/image/src"
],
"@daffodil/design-examples/image-lite": [
"libs/design-examples/image-lite/src"
],
"@daffodil/design-examples/input": [
"libs/design-examples/input/src"
],
Expand Down
1 change: 1 addition & 0 deletions libs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Refer to the [Upgrade Guide](/libs/design/guides/upgrading.md).
* [Native Select](/libs/design/src/atoms/form/native-select/README.md)
* [Quantity Field](/libs/design/src/atoms/form/quantity-field/README.md)
* [Image](/libs/design/image/README.md)
* [Image Lite](/libs/design/image-lite/README.md)
* [Tag](/libs/design/tag/README.md)

### Molecules
Expand Down
58 changes: 58 additions & 0 deletions libs/design/image-lite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Image Lite
Image lite applies Daffodil's image styling directly to a native `<img>` element.

## Overview
Image lite's main purpose is to handle the loading state of an image. It's a thin layer on top of Angular's [`NgOptimizedImage`](https://angular.dev/guide/image-optimization). `NgOptimizedImage` handles the performance work—lazy loading, preloading priority images, and requiring `width` and `height` so the browser can reserve space before the image loads.

<daff-docs-example-viewer example="basic-image-lite"></daff-docs-example-viewer>

## Usage

Import `DAFF_IMAGE_LITE_COMPONENTS` into your component:

```ts
import { Component } from '@angular/core';
import { DAFF_IMAGE_LITE_COMPONENTS } from '@daffodil/design/image-lite';

@Component({
selector: 'custom-component',
templateUrl: './custom-component.component.html',
imports: [
DAFF_IMAGE_LITE_COMPONENTS,
],
})
export class CustomComponent {}
```

> `DAFF_IMAGE_LITE_COMPONENTS` includes `NgOptimizedImage`, so there's no need to import it separately.

## Anatomy
Add the `daff-image` attribute to an `<img>` and set the source with `ngSrc` instead of `src`:

```html
<img daff-image
ngSrc="/assets/basel-exhibition-centre.jpg"
alt="Bottom up view of Basel exhibition centre"
width="1261"
height="946"/>
```

## Required attributes

| Attribute | Description |
| --------- | ----------- |
| `ngSrc` | The image source URL. Replaces `src`; setting both throws an error. |
| `width` | The intrinsic width of the image in pixels. |
| `height` | The intrinsic height of the image in pixels. |

`ngSrc`, `width`, and `height` are required by `NgOptimizedImage` and will throw an error if missing. `alt` is not enforced, but should always be set.

## Features

### Skeleton screen
Use the `skeleton` property to display a placeholder skeleton screen that helps reduce load-time frustration. The skeleton fills the space the image will occupy, so `width` and `height` still need to be set.

<daff-docs-example-viewer example="skeleton-image-lite"></daff-docs-example-viewer>

## Accessibility
Provide concise, meaningful descriptions that convey the content and purpose of the image by using the `alt` attribute.
7 changes: 7 additions & 0 deletions libs/design/image-lite/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
"lib": {
"entryFile": "src/index.ts",
"styleIncludePaths": ["../src/scss"]
}
}
18 changes: 18 additions & 0 deletions libs/design/image-lite/src/image-theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@use '../../scss/theming' as *;

@mixin daff-image-lite-theme($theme) {
$neutral: daff-get-palette($theme, neutral);
$mode: daff-get-theme-mode($theme);

.daff-image-lite {
&.daff-skeleton {
@include daff-light-mode($mode) {
background-color: daff-color($neutral, 20);
}

@include daff-dark-mode($mode) {
background-color: daff-color($neutral, 90);
}
}
}
}
22 changes: 22 additions & 0 deletions libs/design/image-lite/src/image.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@use '../../scss/state';

:host {
display: block;
max-width: 100%;
height: auto;

&.daff-skeleton {
animation: loading 1000ms linear infinite alternate;
object-fit: none;
object-position: -200% -200%;
}
}

@keyframes loading {
from {
opacity: 0.5;
}
to {
opacity: 1;
}
}
58 changes: 58 additions & 0 deletions libs/design/image-lite/src/image.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
Component,
DebugElement,
signal,
} from '@angular/core';
import {
waitForAsync,
ComponentFixture,
TestBed,
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';

import {
DAFF_IMAGE_LITE_COMPONENTS,
DaffImageLiteComponent,
} from '@daffodil/design/image-lite';

@Component({
template: `
<img daff-image [skeleton]="skeleton()">`,
imports: [
DAFF_IMAGE_LITE_COMPONENTS,
],
})

class WrapperComponent {
skeleton = signal(false);
}

describe('@daffodil/design/image | DaffImageLiteComponent', () => {
let wrapper: WrapperComponent;
let component: DaffImageLiteComponent;
let de: DebugElement;
let fixture: ComponentFixture<WrapperComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
WrapperComponent,
],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(WrapperComponent);
wrapper = fixture.componentInstance;
de = fixture.debugElement.query(By.css('img[daff-image]'));
component = de.componentInstance;
});

it('should take skeleton as an input', () => {
wrapper.skeleton.set(true);
fixture.detectChanges();

expect(de.nativeElement.classList.contains('daff-skeleton')).toEqual(true);
});
});
24 changes: 24 additions & 0 deletions libs/design/image-lite/src/image.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint-disable @angular-eslint/component-selector */
import {
Component,
ChangeDetectionStrategy,
} from '@angular/core';

import { DaffSkeletonableDirective } from '@daffodil/design';

@Component({
selector: 'img[daff-image]',
template: '<ng-content></ng-content>',
styleUrl: './image.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
hostDirectives: [
{
directive: DaffSkeletonableDirective,
inputs: ['skeleton'],
},
],
host: {
class: 'daff-image-lite',
},
})
export class DaffImageLiteComponent {}
11 changes: 11 additions & 0 deletions libs/design/image-lite/src/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { NgOptimizedImage } from '@angular/common';

import { DaffImageLiteComponent } from './image.component';

/**
* @docs-private
*/
export const DAFF_IMAGE_LITE_COMPONENTS = <const> [
DaffImageLiteComponent,
NgOptimizedImage,
];
1 change: 1 addition & 0 deletions libs/design/image-lite/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public_api';
2 changes: 2 additions & 0 deletions libs/design/image-lite/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { DaffImageLiteComponent } from './image.component';
export { DAFF_IMAGE_LITE_COMPONENTS } from './image';
2 changes: 2 additions & 0 deletions libs/design/scss/theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
@use '../text-snippet//src/text-snippet-theme' as text-snippet;
@use '../tree/src/tree-theme' as tree;
@use '../toast/src/toast-theme' as toast;
@use '../image-lite//src/image-theme' as image-lite;

//
// Generates the styles of `@daffodil/design`'s component themes.
Expand Down Expand Up @@ -102,6 +103,7 @@
@include hero.daff-hero-theme($theme);
@include list.daff-list-theme($theme);

@include image-lite.daff-image-lite-theme($theme);
@include media-gallery.daff-media-gallery-theme($theme);
@include menu.daff-menu-theme($theme);
@include modal.daff-modal-theme($theme);
Expand Down
3 changes: 3 additions & 0 deletions libs/design/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
"@daffodil/design/image": [
"libs/design/image/src"
],
"@daffodil/design/image-lite": [
"libs/design/image-lite/src"
],
"@daffodil/design/input": [
"libs/design/input/src"
],
Expand Down
Loading