Skip to content

Commit

Permalink
feat(img): adds lazy load image
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat committed May 11, 2018
1 parent 7fa6e43 commit b06c65f
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 0 deletions.
48 changes: 48 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2151,6 +2151,54 @@ declare global {
}


declare global {

namespace StencilComponents {
interface IonImg {
/**
* This attribute defines the alternative text describing the image. Users will see this text displayed if the image URL is wrong, the image is not in one of the supported formats, or if the image is not yet downloaded.
*/
'alt': string;
/**
* The image URL. This attribute is mandatory for the <img> element.
*/
'src': string;
}
}

interface HTMLIonImgElement extends StencilComponents.IonImg, HTMLStencilElement {}

var HTMLIonImgElement: {
prototype: HTMLIonImgElement;
new (): HTMLIonImgElement;
};
interface HTMLElementTagNameMap {
'ion-img': HTMLIonImgElement;
}
interface ElementTagNameMap {
'ion-img': HTMLIonImgElement;
}
namespace JSX {
interface IntrinsicElements {
'ion-img': JSXElements.IonImgAttributes;
}
}
namespace JSXElements {
export interface IonImgAttributes extends HTMLAttributes {
/**
* This attribute defines the alternative text describing the image. Users will see this text displayed if the image URL is wrong, the image is not in one of the supported formats, or if the image is not yet downloaded.
*/
'alt'?: string;
'onIonImgDidLoad'?: (event: CustomEvent<void>) => void;
/**
* The image URL. This attribute is mandatory for the <img> element.
*/
'src'?: string;
}
}
}


declare global {

namespace StencilComponents {
Expand Down
12 changes: 12 additions & 0 deletions core/src/components/img/img.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

ion-img {
display: block;
}

ion-img > img {
width: 100%;
height: 100%;

object-fit: inherit;
object-position: inherit;
}
77 changes: 77 additions & 0 deletions core/src/components/img/img.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Component, Element, Event, EventEmitter, Prop, State, Watch } from '@stencil/core';


@Component({
tag: 'ion-img',
styleUrl: 'img.scss'
})
export class Img {

private io?: IntersectionObserver;

@Element() el!: HTMLElement;

@State() loadSrc?: string;

/**
* This attribute defines the alternative text describing the image.
* Users will see this text displayed if the image URL is wrong,
* the image is not in one of the supported formats, or if the image is not yet downloaded.
*/
@Prop() alt?: string;

/**
* The image URL. This attribute is mandatory for the <img> element.
*/
@Prop() src?: string;
@Watch('src')
srcChanged() {
this.addIO();
}

@Event() ionImgDidLoad!: EventEmitter<void>;

componentDidLoad() {
this.addIO();
}

private addIO() {
if (!this.src) {
return;
}
if ('IntersectionObserver' in window) {
this.removeIO();
this.io = new IntersectionObserver((data) => {
// because there will only ever be one instance
// of the element we are observing
// we can just use data[0]
if (data[0].isIntersecting) {
this.loadSrc = this.src;
this.removeIO();
this.ionImgDidLoad.emit();
}
});

this.io.observe(this.el);
} else {
// fall back to setTimeout for Safari and IE
setTimeout(() => this.loadSrc = this.src, 200);
}
}

private removeIO() {
if (this.io) {
this.io.disconnect();
this.io = undefined;
}
}

render() {
return (
<img
src={this.loadSrc}
alt={this.alt}
decoding="async"></img>
);
}
}
145 changes: 145 additions & 0 deletions core/src/components/img/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# ion-fab

Fabs are container elements that contain one or more fab buttons. They should be placed in a fixed position that does not scroll with the content. The following attributes can be used to position the fab with respect to the content:

| Value | Alignment | Details |
|--------------|------------|---------------------------------------------------------------------------|
| `top` | vertical | Places the container at the top of the content. |
| `bottom` | vertical | Places the container at the bottom of the content. |
| `middle` | vertical | Places the container in the middle vertically. |
| `edge` | vertical | Used to place the container between the content and the header/footer. |
| `left` | horizontal | Places the container on the left. |
| `right` | horizontal | Places the container on the right. |
| `center` | horizontal | Places the container in the center horizontally. |

The fab should have one main fab button. Fabs can also contain fab lists which contain related buttons that show when the main fab button is clicked. The same fab container can contain several [fab list](../../fab-list/FabList) elements with different side values.

```html
<ion-content>
<!-- fab placed to the top right -->
<ion-fab top right slot="fixed">
<ion-fab-button>
<ion-icon name="add"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- fab placed to the bottom right -->
<ion-fab bottom right slot="fixed">
<ion-fab-button>
<ion-icon name="arrow-dropleft"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- fab placed to the top left -->
<ion-fab top left slot="fixed">
<ion-fab-button>
<ion-icon name="arrow-dropright"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- fab placed to the bottom left -->
<ion-fab bottom left slot="fixed">
<ion-fab-button>
<ion-icon name="arrow-dropup"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- fab placed to the left and middle -->
<ion-fab left middle slot="fixed">
<ion-fab-button>
<ion-icon name="share"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- fab placed to the right and middle -->
<ion-fab right middle slot="fixed">
<ion-fab-button>
<ion-icon name="add"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- fab placed to the top and right and on the top edge of the content overlapping header -->
<ion-fab top right edge slot="fixed">
<ion-fab-button>
<ion-icon name="person"></ion-icon>
</ion-fab-button>
</ion-fab>

<!-- fab placed to the bottom and left and on the bottom edge of the content overlapping footer with a list to the right -->
<ion-fab bottom left edge slot="fixed">
<ion-fab-button>
<ion-icon name="settings"></ion-icon>
</ion-fab-button>
<ion-fab-list side="end">
<ion-fab-button><ion-icon name="logo-vimeo"></ion-icon></ion-fab-button>
</ion-fab-list>
</ion-fab>

<!-- fab placed in the center of the content with a list on each side -->
<ion-fab center middle slot="fixed">
<ion-fab-button><ion-icon name="share"></ion-icon></ion-fab-button>
<ion-fab-list side="top">
<ion-fab-button><ion-icon name="logo-vimeo"></ion-icon></ion-fab-button>
</ion-fab-list>
<ion-fab-list side="bottom">
<ion-fab-button><ion-icon name="logo-facebook"></ion-icon></ion-fab-button>
</ion-fab-list>
<ion-fab-list side="start">
<ion-fab-button><ion-icon name="logo-googleplus"></ion-icon></ion-fab-button>
</ion-fab-list>
<ion-fab-list side="end">
<ion-fab-button><ion-icon name="logo-twitter"></ion-icon></ion-fab-button>
</ion-fab-list>
</ion-fab>
</ion-content>
```


<!-- Auto Generated Below -->


## Properties

#### alt

string

This attribute defines the alternative text describing the image.
Users will see this text displayed if the image URL is wrong,
the image is not in one of the supported formats, or if the image is not yet downloaded.


#### src

string

The image URL. This attribute is mandatory for the <img> element.


## Attributes

#### alt

string

This attribute defines the alternative text describing the image.
Users will see this text displayed if the image URL is wrong,
the image is not in one of the supported formats, or if the image is not yet downloaded.


#### src

string

The image URL. This attribute is mandatory for the <img> element.


## Events

#### ionImgDidLoad



----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
19 changes: 19 additions & 0 deletions core/src/components/img/test/basic/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

const { By, until } = require('selenium-webdriver');
const { register, Page, platforms } = require('../../../../../scripts/e2e');

class E2ETestPage extends Page {
constructor(driver, platform) {
super(driver, `http://localhost:3333/src/components/fab/test/basic?ionic:mode=${platform}`);
}
}

platforms.forEach(platform => {
describe('fab/basic', () => {
register('should init', driver => {
const page = new E2ETestPage(driver, platform);
return page.navigate('#content');
});
});
});
62 changes: 62 additions & 0 deletions core/src/components/img/test/basic/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Floating Action Button - Basic</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<script src="/dist/ionic.js"></script>
</head>
<body>
<ion-app>

<ion-header>
<ion-toolbar>
<ion-title>Img - Basic</ion-title>
</ion-toolbar>
</ion-header>

<ion-content padding>
<f></f>
<f></f>
<ion-img src="https://i.ytimg.com/vi/rq6M3imPgW4/maxresdefault.jpg"></ion-img>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<ion-img id="hidden-car" src="https://www.ericpetersautos.com/wp-content/uploads/2017/11/1961-Ferrari-250-GT-SWB-Berlinetta-by-Scaglietti_Erik-Fuller-c-2016-Courtesy-RM-Sothebys-1.jpg"></ion-img>

</ion-content>

<ion-footer>
<ion-toolbar>
<ion-title>Footer</ion-title>
</ion-toolbar>
</ion-footer>

<style>
f {
display: block;
margin: 15px auto;
max-width: 150px;
height: 150px;
background: blue;
}

#hidden-car {
width: 300px;
}

f:last-of-type {
background: yellow;
}
</style>
</ion-app>
<script>
document.body.addEventListener('ionImgDidLoad', (event) => {
console.log('image did load', event.target);
});
</script>
</body>
</html>

0 comments on commit b06c65f

Please sign in to comment.