Skip to content

Commit 06fa4a5

Browse files
authored
fix(icon): add :dir fallback (#1223)
1 parent 3992b01 commit 06fa4a5

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

src/components/icon/icon.css

+39
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,54 @@ svg {
3636
/* Icon RTL
3737
* -----------------------------------------------------------
3838
*/
39+
40+
/**
41+
* Safari <16.4 incorrectly reports
42+
* that it supports :dir(rtl) when it does not.
43+
* This @supports statement lets us target
44+
* WebKit browsers to apply the RTL fallback.
45+
* -webkit-named-image only exists on WebKit.
46+
* For WebKit browsers that do support :dir(rtl)
47+
* (i.e. Safari >= 16.4) then the :dir(rtl)
48+
* code farther down on the page will take
49+
* effect and override this fallback.
50+
*/
51+
@supports (background: -webkit-named-image(i)) {
52+
:host(.icon-rtl) .icon-inner {
53+
transform: scaleX(-1);
54+
}
55+
}
56+
57+
/**
58+
* Fallback for browsers that support
59+
* neither :host-context nor :dir.
60+
* The icon will not react to dir
61+
* changes, but it will at least
62+
* respect the dir on component load.
63+
*/
64+
@supports not selector(:dir(rtl)) and selector(:host-context([dir='rtl'])) {
65+
:host(.icon-rtl) .icon-inner {
66+
transform: scaleX(-1);
67+
}
68+
}
3969

4070
/* :host-context is supported in chromium; :dir is supported in safari & firefox */
4171
:host(.flip-rtl):host-context([dir='rtl']) .icon-inner {
4272
transform: scaleX(-1);
4373
}
74+
4475
@supports selector(:dir(rtl)) {
4576
:host(.flip-rtl:dir(rtl)) .icon-inner {
4677
transform: scaleX(-1);
4778
}
79+
/**
80+
* This is needed for WebKit otherwise the fallback
81+
* will always cause the icon to be flipped if the document
82+
* loads in RTL.
83+
*/
84+
:host(.flip-rtl:dir(ltr)) .icon-inner {
85+
transform: scaleX(1);
86+
}
4887
}
4988

5089
/* Icon Sizes

src/components/icon/icon.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Build, Component, Element, Host, Prop, State, Watch, h } from '@stencil/core';
22
import { getSvgContent, ioniconContent } from './request';
3-
import { getName, getUrl, inheritAttributes } from './utils';
3+
import { getName, getUrl, inheritAttributes, isRTL } from './utils';
44

55
@Component({
66
tag: 'ion-icon',
@@ -145,7 +145,7 @@ export class Icon {
145145
}
146146

147147
render() {
148-
const { flipRtl, iconName, inheritedAttributes } = this;
148+
const { flipRtl, iconName, inheritedAttributes, el } = this;
149149
const mode = this.mode || 'md';
150150
// we have designated that arrows & chevrons should automatically flip (unless flip-rtl is set to false) because "back" is left in ltr and right in rtl, and "forward" is the opposite
151151
const shouldAutoFlip = iconName
@@ -162,6 +162,7 @@ export class Icon {
162162
...createColorClasses(this.color),
163163
[`icon-${this.size}`]: !!this.size,
164164
'flip-rtl': shouldBeFlippable,
165+
'icon-rtl': shouldBeFlippable && isRTL(el)
165166
}}
166167
{...inheritedAttributes}
167168
>

src/components/icon/test/icon.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('icon', () => {
2424
});
2525

2626
expect(root).toEqualHtml(`
27-
<ion-icon class="md flip-rtl" name="chevron-forward" role="img" aria-hidden="true">
27+
<ion-icon class="md flip-rtl icon-rtl" name="chevron-forward" role="img" aria-hidden="true">
2828
<mock:shadow-root>
2929
<div class="icon-inner"></div>
3030
</mock:shadow-root>

src/components/icon/utils.ts

+14
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,17 @@ export const inheritAttributes = (el: HTMLElement, attributes: string[] = []) =>
140140

141141
return attributeObject;
142142
}
143+
144+
/**
145+
* Returns `true` if the document or host element
146+
* has a `dir` set to `rtl`. The host value will always
147+
* take priority over the root document value.
148+
*/
149+
export const isRTL = (hostEl?: Pick<HTMLElement, 'dir'>) => {
150+
if (hostEl) {
151+
if (hostEl.dir !== '') {
152+
return hostEl.dir.toLowerCase() === 'rtl';
153+
}
154+
}
155+
return document?.dir.toLowerCase() === 'rtl';
156+
};

0 commit comments

Comments
 (0)