-
Notifications
You must be signed in to change notification settings - Fork 0
/
plmg-svg-icon.tsx
124 lines (111 loc) · 2.92 KB
/
plmg-svg-icon.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import {
Component,
Element,
State,
Prop,
h,
Watch,
Build,
} from '@stencil/core';
import { fetchIcon, supportsIntersectionObserver } from './utils';
@Component({
tag: 'plmg-svg-icon',
styleUrl: 'plmg-svg-icon.scss',
shadow: true,
})
export class SvgIcon {
@Element() el: HTMLElement;
/**
* Define icon's color.
*
* Can be any valid CSS color value.
*
* By default, the icon will have the same color as the parent's element.
*/
@Prop() color: string | undefined;
/**
* Define icon by its name.
* Name must be one of the existing icon: https://components.ducky.eco/?path=/story/component-svgicon--all-icons
*
* Default: NULL
*/
@Prop() icon: string = null;
@Watch('icon')
private async loadIconPathData(): Promise<void> {
const { icon, visible } = this;
if (!Build.isBrowser || !icon || !visible) {
return;
}
this.pathData = await fetchIcon({ icon });
}
/**
* Define the icon's size.
*
* Allowed values: <value><unit>
*
* Examples:
* - 1em
* - 42px
*
* Default: 1em
*/
@Prop() size: string = '1em';
@State() private pathData: string;
@State() private visible = false;
private intersectionObserver: IntersectionObserver;
// When connected to the DOM
connectedCallback(): void {
this.waitUntilVisible(async () => {
this.visible = true;
await this.loadIconPathData();
});
}
// When removed from the DOM
disconnectedCallback(): void {
if (this.intersectionObserver) {
this.intersectionObserver.disconnect();
this.intersectionObserver = null;
}
}
// When first added to the DOM
async componentWillLoad(): Promise<void> {
await this.loadIconPathData();
}
render() {
return (
<div
innerHTML={this.pathData}
class={{ ['plmg-svg-icon']: true }}
style={{
fontSize: this.size,
color: this.color ?? 'inherit',
}}
/>
);
}
private waitUntilVisible(callback: () => void): void {
if (!supportsIntersectionObserver) {
callback();
return;
}
// Create an intersection observer for this component. By default, the element to intersect with is the viewport
this.intersectionObserver = new IntersectionObserver(
(entries) => {
// Entry is every element we are observing, in our case, only this component
entries.forEach((entry) => {
// If the component is intersecting the viewport
if (entry.isIntersecting) {
// Stop listening for intersection, as we are already going to load the icon
this.intersectionObserver.disconnect();
this.intersectionObserver = null;
// Load the icon
callback();
}
});
},
{ rootMargin: '50px' }
);
// Start observing when the component intersects within 50px of the viewport
this.intersectionObserver.observe(this.el);
}
}