Skip to content

Commit

Permalink
feat(icon): expose 'inner-html' and 'svg' as shadow parts
Browse files Browse the repository at this point in the history
Also
- add usage examples to index.html
- update icon readme
  • Loading branch information
Juarrow committed Dec 8, 2021
1 parent 19dd664 commit b572c8d
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 7 deletions.
61 changes: 54 additions & 7 deletions src/components/icon/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class Icon {
private io?: IntersectionObserver;
private iconName: string | null = null;
private inheritedAttributes: { [k: string]: any } = {};
private svgContentInjected: boolean = false;

@Element() el!: HTMLElement;

Expand Down Expand Up @@ -80,7 +81,7 @@ export class Icon {
* @default true
*/
@Prop() sanitize = true;

componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
}
Expand Down Expand Up @@ -122,13 +123,43 @@ export class Icon {
cb();
}
}

private hasAriaHidden = () => {
const { el } = this;

return el.hasAttribute('aria-hidden') && el.getAttribute('aria-hidden') === 'true';
}

private createSvgElement(): SVGElement | null {
if (this.svgContent) {
const template = document.createElement("template");
template.innerHTML = this.svgContent;

// Extract the first element from the template.
// It should be our <svg>.
const node = template.content.firstChild;
if (node) {
if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName === 'svg') {
const svg = node as SVGElement;

// Add the shadow part
svg.setAttribute('part', 'svg');

return svg;
}
}
}

return null;
}

private injectSvgElement(svgElement: SVGElement) {
const el = (this.el.shadowRoot as ShadowRoot).querySelector('.icon-inner')
if (el) {
el.appendChild(svgElement);
}
}

@Watch('name')
@Watch('src')
@Watch('icon')
Expand Down Expand Up @@ -186,13 +217,29 @@ export class Icon {
{...inheritedAttributes}
>
{Build.isBrowser && this.svgContent ? (
<div class="icon-inner" innerHTML={this.svgContent}></div>
<div class="icon-inner" part="icon-inner" >
</div>
) : (
<div class="icon-inner"></div>
)}
</Host>
);
}

componentDidRender() {
/**
* If it has not been done already, create & inject the <SVG> element
* into `div.icon-inner`.
*/
if (!this.svgContentInjected) {
const svgElement = this.createSvgElement();
if (svgElement) {
this.injectSvgElement(svgElement);

this.svgContentInjected = true;
}
}
}
}

const getIonMode = () =>
Expand All @@ -201,8 +248,8 @@ const getIonMode = () =>
const createColorClasses = (color: string | undefined) => {
return color
? {
'ion-color': true,
[`ion-color-${color}`]: true,
}
'ion-color': true,
[`ion-color-${color}`]: true,
}
: null;
};
8 changes: 8 additions & 0 deletions src/components/icon/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
| `src` | `src` | Specifies the exact `src` of an SVG file to use. | `string \| undefined` | `undefined` |


## Shadow Parts

| Part | Description |
| -------------- | ----------- |
| `"icon-inner"` | The container that wraps the SVG element |
| `"svg"` | The svg element |


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

*Built with [StencilJS](https://stenciljs.com/)*
12 changes: 12 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ <h2>Sanitized (shouldn't show)</h2>
<h2>Not Sanitized (should show)</h2>
<ion-icon sanitize="false" src="./assets/no-sanitize.svg"></ion-icon>

<h2>Accessing ::part()'s</h2>
<ion-icon id="part-svg" src="./assets/bug_report.svg"></ion-icon>
<ion-icon id="part-icon-inner" src="./assets/chat.svg"></ion-icon>

<p>
<a href="./cheatsheet.html">Cheatsheet</a>
</p>
Expand All @@ -156,6 +160,14 @@ <h2>Not Sanitized (should show)</h2>
stroke: red;
fill: blue;
}

#part-svg::part(svg) {
fill: rebeccapurple;
}

#part-icon-inner::part(icon-inner) {
fill: aliceblue;
}
</style>
</body>
</html>

0 comments on commit b572c8d

Please sign in to comment.