Skip to content

Commit 1f9b029

Browse files
Merge pull request #10 from irohalab/responsive-image
add responsive image, this component is extract from mira-ui
2 parents 1d47c6a + 6b7eae2 commit 1f9b029

File tree

7 files changed

+466
-2
lines changed

7 files changed

+466
-2
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "deneb-ui",
3-
"version": "4.2.0",
3+
"version": "4.3.0",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve --project Deneb-UI-Demo",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// select closet parent element
2+
export function closest(el: any, selector: string) {
3+
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
4+
5+
while (el) {
6+
if (matchesSelector.call(el, selector)) {
7+
return el;
8+
} else {
9+
el = el.parentElement;
10+
}
11+
}
12+
return null;
13+
}
14+
15+
export function getRemPixel(remValue: number): number {
16+
return remValue * parseFloat(window.getComputedStyle(document.body).getPropertyValue('font-size').match(/(\d+(?:\.\d+)?)px/)[1]);
17+
}
18+
19+
/**
20+
* get the vw in pixel
21+
* @param value
22+
*/
23+
export function getVwInPixel(value: number): number {
24+
let w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
25+
return value / 100 * w;
26+
}
27+
28+
export function getVhInPixel(value: number): number {
29+
let h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
30+
return value / 100 * h;
31+
}

projects/irohalab/deneb-ui/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ const UI_MODULES = [
1919
UIScrollbarModule,
2020
UIDropdownModule,
2121
UIToggleModule,
22-
UIPopoverModule
22+
UIPopoverModule,
23+
2324
];
2425

2526
@NgModule({
@@ -40,5 +41,6 @@ export * from './scrollbar';
4041
export * from './dropdown';
4142
export * from './toggle';
4243
export * from './popover';
44+
export * from './responsive-image';
4345

4446
export * from './dark-theme.service';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { NgModule } from '@angular/core';
2+
import { UIResponsiveImage } from './responsive-image.directive';
3+
import { UIResponsiveService } from './responsive.service';
4+
import { UIResponsiveImageWrapper } from './responsive-image-wrapper';
5+
6+
@NgModule({
7+
declarations: [UIResponsiveImage, UIResponsiveImageWrapper],
8+
providers: [UIResponsiveService],
9+
exports: [UIResponsiveImage, UIResponsiveImageWrapper]
10+
})
11+
export class UIResponsiveImageModule {
12+
13+
}
14+
15+
export * from './responsive.service';
16+
export * from './responsive-image.directive'
17+
export * from './responsive-image-wrapper';
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import { Component, EventEmitter, Input, Output } from '@angular/core';
2+
import { ResponsiveDimension, UIResponsiveImage } from './responsive-image.directive';
3+
4+
export interface ResponsiveWrapperSize {
5+
/**
6+
* can be rem, em, pixel, %, vw, auto
7+
*/
8+
width?: string;
9+
/**
10+
* can be rem, em, pixel, %, vh, auto
11+
*/
12+
height?: string;
13+
/**
14+
* A value represents height / width
15+
* If ratio is used, width must have value and cannot be auto,
16+
* and height will be ignored.
17+
*/
18+
ratio?: number;
19+
20+
/**
21+
* the image original width and height in pixel
22+
*/
23+
originalWidth: number;
24+
originalHeight: number;
25+
}
26+
27+
export const DEFAULT_HIDDEN_OPACITY = 0.01;
28+
29+
@Component({
30+
selector: 'ui-responsive-image',
31+
template: `<img class="responsive-image"
32+
[originalSrc]="src"
33+
[dimension]="dimension"
34+
[style.width]="imageWidth"
35+
[style.height]="imageHeight"
36+
[style.position]="imagePosition"
37+
[style.opacity]="imageOpacity"
38+
(imageLoad)="onLoad($event)"
39+
(imageError)="onError($event)">`,
40+
styles: [`
41+
:host {
42+
box-sizing: border-box;
43+
position: relative;
44+
}
45+
46+
.responsive-image {
47+
display: block;
48+
object-fit: cover;
49+
transition: opacity 500ms;
50+
}
51+
`],
52+
host: {
53+
'[style.display]': 'display',
54+
'[style.width]': 'hostWidth',
55+
'[style.height]': 'hostHeight',
56+
'[style.paddingBottom]': 'hostPaddingBottom',
57+
'[style.background]': 'background'
58+
}
59+
})
60+
export class UIResponsiveImageWrapper {
61+
62+
dimension: ResponsiveDimension;
63+
64+
@Input()
65+
src: string;
66+
67+
@Input()
68+
display: string = 'block';
69+
70+
@Input()
71+
set size(s: ResponsiveWrapperSize) {
72+
let dimen: ResponsiveDimension;
73+
if (s.ratio) {
74+
this.imageWidth = '100%';
75+
this.imageHeight = '100%';
76+
this.hostWidth = s.width;
77+
this.hostHeight = '0';
78+
this.hostPaddingBottom = `${s.ratio * 100}%`;
79+
this.imagePosition = 'absolute';
80+
let widthInPixel = UIResponsiveImage.getPx(s.width);
81+
if (widthInPixel !== 0) {
82+
dimen = {
83+
width: `${widthInPixel}px`,
84+
height: `${widthInPixel * s.ratio}px`,
85+
originalWidth: s.originalWidth,
86+
originalHeight: s.originalHeight
87+
};
88+
} else {
89+
if (s.originalHeight / s.originalWidth < s.ratio) {
90+
dimen = {
91+
width: 'auto',
92+
height: '100%',
93+
originalWidth: s.originalWidth,
94+
originalHeight: s.originalHeight
95+
}
96+
} else {
97+
dimen = {
98+
width: '100%',
99+
height: 'auto',
100+
originalWidth: s.originalWidth,
101+
originalHeight: s.originalHeight
102+
}
103+
}
104+
}
105+
} else {
106+
this.hostWidth = s.width;
107+
this.hostHeight = s.height;
108+
dimen = {
109+
width: 'auto',
110+
height: 'auto',
111+
originalWidth: s.originalWidth,
112+
originalHeight: s.originalHeight
113+
};
114+
if (s.width === 'auto') {
115+
this.imageWidth = 'auto';
116+
} else {
117+
this.imageWidth = '100%';
118+
let widthInPixel = Math.round(UIResponsiveImage.getPx(s.width));
119+
if (widthInPixel !== 0) {
120+
dimen.width = `${widthInPixel}px`;
121+
} else {
122+
dimen.width = '100%';
123+
}
124+
}
125+
126+
if (s.height === 'auto') {
127+
this.imageHeight = 'auto';
128+
} else {
129+
this.imageHeight = '100%';
130+
let heightInPixel = Math.round(UIResponsiveImage.getPx(s.height));
131+
if (heightInPixel !== 0) {
132+
dimen.height = `${heightInPixel}px`;
133+
} else {
134+
dimen.height = '100%';
135+
}
136+
}
137+
}
138+
this.dimension = dimen;
139+
};
140+
141+
@Input()
142+
background: string = '#cccccc'; // color
143+
144+
hostWidth: string;
145+
hostHeight: string;
146+
hostPaddingBottom: string = '0';
147+
148+
imageWidth: string;
149+
imageHeight: string;
150+
imagePosition: string = 'static';
151+
152+
imageOpacity: number = DEFAULT_HIDDEN_OPACITY;
153+
154+
@Output()
155+
imageLoad = new EventEmitter<Event>();
156+
157+
@Output()
158+
imageError = new EventEmitter<Event>();
159+
160+
onLoad(event: Event) {
161+
this.imageOpacity = 1;
162+
this.imageLoad.emit(event);
163+
}
164+
165+
onError(event: Event) {
166+
this.imageOpacity = DEFAULT_HIDDEN_OPACITY;
167+
this.imageError.emit(event);
168+
}
169+
}

0 commit comments

Comments
 (0)