Skip to content

Commit b572c8d

Browse files
committed
feat(icon): expose 'inner-html' and 'svg' as shadow parts
Also - add usage examples to index.html - update icon readme
1 parent 19dd664 commit b572c8d

File tree

3 files changed

+74
-7
lines changed

3 files changed

+74
-7
lines changed

src/components/icon/icon.tsx

+54-7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export class Icon {
1212
private io?: IntersectionObserver;
1313
private iconName: string | null = null;
1414
private inheritedAttributes: { [k: string]: any } = {};
15+
private svgContentInjected: boolean = false;
1516

1617
@Element() el!: HTMLElement;
1718

@@ -80,7 +81,7 @@ export class Icon {
8081
* @default true
8182
*/
8283
@Prop() sanitize = true;
83-
84+
8485
componentWillLoad() {
8586
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
8687
}
@@ -122,13 +123,43 @@ export class Icon {
122123
cb();
123124
}
124125
}
125-
126+
126127
private hasAriaHidden = () => {
127128
const { el } = this;
128-
129+
129130
return el.hasAttribute('aria-hidden') && el.getAttribute('aria-hidden') === 'true';
130131
}
131132

133+
private createSvgElement(): SVGElement | null {
134+
if (this.svgContent) {
135+
const template = document.createElement("template");
136+
template.innerHTML = this.svgContent;
137+
138+
// Extract the first element from the template.
139+
// It should be our <svg>.
140+
const node = template.content.firstChild;
141+
if (node) {
142+
if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName === 'svg') {
143+
const svg = node as SVGElement;
144+
145+
// Add the shadow part
146+
svg.setAttribute('part', 'svg');
147+
148+
return svg;
149+
}
150+
}
151+
}
152+
153+
return null;
154+
}
155+
156+
private injectSvgElement(svgElement: SVGElement) {
157+
const el = (this.el.shadowRoot as ShadowRoot).querySelector('.icon-inner')
158+
if (el) {
159+
el.appendChild(svgElement);
160+
}
161+
}
162+
132163
@Watch('name')
133164
@Watch('src')
134165
@Watch('icon')
@@ -186,13 +217,29 @@ export class Icon {
186217
{...inheritedAttributes}
187218
>
188219
{Build.isBrowser && this.svgContent ? (
189-
<div class="icon-inner" innerHTML={this.svgContent}></div>
220+
<div class="icon-inner" part="icon-inner" >
221+
</div>
190222
) : (
191223
<div class="icon-inner"></div>
192224
)}
193225
</Host>
194226
);
195227
}
228+
229+
componentDidRender() {
230+
/**
231+
* If it has not been done already, create & inject the <SVG> element
232+
* into `div.icon-inner`.
233+
*/
234+
if (!this.svgContentInjected) {
235+
const svgElement = this.createSvgElement();
236+
if (svgElement) {
237+
this.injectSvgElement(svgElement);
238+
239+
this.svgContentInjected = true;
240+
}
241+
}
242+
}
196243
}
197244

198245
const getIonMode = () =>
@@ -201,8 +248,8 @@ const getIonMode = () =>
201248
const createColorClasses = (color: string | undefined) => {
202249
return color
203250
? {
204-
'ion-color': true,
205-
[`ion-color-${color}`]: true,
206-
}
251+
'ion-color': true,
252+
[`ion-color-${color}`]: true,
253+
}
207254
: null;
208255
};

src/components/icon/readme.md

+8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222
| `src` | `src` | Specifies the exact `src` of an SVG file to use. | `string \| undefined` | `undefined` |
2323

2424

25+
## Shadow Parts
26+
27+
| Part | Description |
28+
| -------------- | ----------- |
29+
| `"icon-inner"` | The container that wraps the SVG element |
30+
| `"svg"` | The svg element |
31+
32+
2533
----------------------------------------------
2634

2735
*Built with [StencilJS](https://stenciljs.com/)*

src/index.html

+12
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ <h2>Sanitized (shouldn't show)</h2>
130130
<h2>Not Sanitized (should show)</h2>
131131
<ion-icon sanitize="false" src="./assets/no-sanitize.svg"></ion-icon>
132132

133+
<h2>Accessing ::part()'s</h2>
134+
<ion-icon id="part-svg" src="./assets/bug_report.svg"></ion-icon>
135+
<ion-icon id="part-icon-inner" src="./assets/chat.svg"></ion-icon>
136+
133137
<p>
134138
<a href="./cheatsheet.html">Cheatsheet</a>
135139
</p>
@@ -156,6 +160,14 @@ <h2>Not Sanitized (should show)</h2>
156160
stroke: red;
157161
fill: blue;
158162
}
163+
164+
#part-svg::part(svg) {
165+
fill: rebeccapurple;
166+
}
167+
168+
#part-icon-inner::part(icon-inner) {
169+
fill: aliceblue;
170+
}
159171
</style>
160172
</body>
161173
</html>

0 commit comments

Comments
 (0)