Skip to content

Commit 29a991f

Browse files
authored
feat(framework): make icons RTL aware (#1833)
Currently all icons are mirrored in RTL, but some of the icons should not be mirrored in RTL. The best example is the "accept" icon (checkmark) which does not make sense when mirrored. Now we keep info as "ltr" field in the SAP-Icons.json, which means an icon should remain as it is in RTL, or in other words it is always LTR. We can call the field differently in the SVGRegistry if we want - suppressMirroring, noMirror. I named it "ltr" everywhere for now. FIXES: #1831
1 parent 5438c66 commit 29a991f

File tree

8 files changed

+2182
-682
lines changed

8 files changed

+2182
-682
lines changed

packages/base/src/SVGIconRegistry.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ const calcKey = (name, collection) => {
1414
return `${collection}:${name}`;
1515
};
1616

17-
const registerIcon = (name, { pathData, accData, collection } = {}) => {
17+
const registerIcon = (name, { pathData, ltr, accData, collection } = {}) => { // eslint-disable-line
1818
const key = calcKey(name, collection);
19-
registry.set(key, { pathData, accData });
19+
registry.set(key, { pathData, ltr, accData });
2020
};
2121

2222
const getIconDataSync = (name, collection = DEFAULT_COLLECTION) => {

packages/base/src/asset-registries/Icons.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ const registerIconBundle = async (collectionName, bundleData) => {
1717

1818
const fillRegistry = bundleData => {
1919
Object.keys(bundleData.data).forEach(iconName => {
20-
registerIcon(iconName, { pathData: bundleData.data[iconName], accData: bundleData.accData[iconName], collection: bundleData.collection });
20+
const iconData = bundleData.data[iconName];
21+
22+
registerIcon(iconName, {
23+
pathData: iconData.path,
24+
ltr: iconData.ltr,
25+
accData: iconData.acc,
26+
collection: bundleData.collection,
27+
});
2128
});
2229
};
2330

packages/icons/src/icon-collections/SAP-icons.json

+2,064-661
Large diffs are not rendered by default.

packages/main/src/CheckBox.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
>
1616
<div id="{{_id}}-CbBg" class="ui5-checkbox-inner">
1717
{{#if checked}}
18-
<ui5-icon name="accept" class="ui5-checkbox-icon" dir="ltr"></ui5-icon>
18+
<ui5-icon name="accept" class="ui5-checkbox-icon"></ui5-icon>
1919
{{/if}}
2020

2121
<input id="{{_id}}-CB" type='checkbox' ?checked="{{checked}}" ?readonly="{{readonly}}" ?disabled="{{disabled}}" data-sap-no-tab-ref/>

packages/main/src/Icon.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<svg
22
class="ui5-icon-root"
33
tabindex="{{tabIndex}}"
4-
dir="{{effectiveDir}}"
4+
dir="{{_dir}}"
55
viewBox="0 0 512 512"
66
role="{{role}}"
77
focusable="false"

packages/main/src/Icon.js

+13
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,18 @@ class Icon extends UI5Element {
192192
}
193193
}
194194

195+
get _dir() {
196+
if (!this.effectiveDir) {
197+
return;
198+
}
199+
200+
if (this.ltr) {
201+
return "ltr";
202+
}
203+
204+
return this.effectiveDir;
205+
}
206+
195207
get tabIndex() {
196208
return this.interactive ? "0" : "-1";
197209
}
@@ -249,6 +261,7 @@ class Icon extends UI5Element {
249261

250262
this.pathData = iconData.pathData;
251263
this.accData = iconData.accData;
264+
this.ltr = iconData.ltr;
252265
}
253266

254267
get hasIconTooltip() {

packages/main/test/pages/Icon.html

+80-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
<script src="../../resources/bundle.esm.js" type="module"></script>
2121
<script nomodule src="../../resources/bundle.es5.js"></script>
2222

23+
<style>
24+
section {
25+
border: dashed 2px red;
26+
}
27+
</style>
2328
</head>
2429

2530
<body style="background-color: var(--sapBackgroundColor);">
@@ -43,7 +48,7 @@
4348
<div id="content"></div>
4449
<ui5-icon name="appointment-2"></ui5-icon>
4550
<ui5-icon name="appointment-2"></ui5-icon>
46-
<!-- <ui5-icon name="appointment-2"></ui5-icon>
51+
<ui5-icon name="appointment-2"></ui5-icon>
4752
<ui5-icon name="appointment-2"></ui5-icon> -->
4853

4954
<br />
@@ -115,7 +120,7 @@
115120
iconNames.forEach(iconName => {
116121
var icon = document.createElement("ui5-icon");
117122
icon.name = iconName;
118-
document.body.appendChild(icon);
123+
allIcons.appendChild(icon);
119124
});
120125
})();
121126
</script>
@@ -125,10 +130,82 @@
125130
iconNames.forEach(function(iconName) {
126131
var icon = document.createElement("ui5-icon");
127132
icon.name = iconName;
128-
document.body.appendChild(icon);
133+
allIcons.appendChild(icon);
129134
});
130135
});
131136
</script>
132137

138+
139+
<section id="allIcons">
140+
<h3>All icons</h3>
141+
</section>
142+
143+
<section class="sectionLTR1" dir="ltr">
144+
<h3>Icons in ltr</h3>
145+
<ui5-icon name="accept"></ui5-icon>
146+
<ui5-icon name="sales-document"></ui5-icon>
147+
<ui5-icon name="sales-notification"></ui5-icon>
148+
<ui5-icon name="search"></ui5-icon>
149+
<ui5-icon name="simple-payment"></ui5-icon>
150+
<ui5-icon name="sound-loud"></ui5-icon>
151+
<ui5-icon name="sound-off"></ui5-icon>
152+
<ui5-icon name="sound"></ui5-icon>
153+
<ui5-icon name="sys-help"></ui5-icon>
154+
<ui5-icon name="text-align-justified"></ui5-icon>
155+
<ui5-icon name="text-align-center"></ui5-icon>
156+
<ui5-icon name="text-align-left"></ui5-icon>
157+
<ui5-icon name="text-align-right"></ui5-icon>
158+
<ui5-icon name="text-formatting"></ui5-icon>
159+
<ui5-icon name="line-chart-time-axis"></ui5-icon>
160+
<br>
161+
<ui5-checkbox checked></ui5-checkbox>
162+
</section>
163+
164+
<section class="sectionRTLNotMirrored" dir="rtl">
165+
<h3>Icons in RTL, but not mirrored</h3>
166+
<ui5-icon name="accept"></ui5-icon>
167+
<ui5-icon name="sales-document"></ui5-icon>
168+
<ui5-icon name="sales-notification"></ui5-icon>
169+
<ui5-icon name="search"></ui5-icon>
170+
<ui5-icon name="simple-payment"></ui5-icon>
171+
<ui5-icon name="sound-loud"></ui5-icon>
172+
<ui5-icon name="sound-off"></ui5-icon>
173+
<ui5-icon name="sound"></ui5-icon>
174+
<ui5-icon name="sys-help"></ui5-icon>
175+
<ui5-icon name="text-align-justified"></ui5-icon>
176+
<ui5-icon name="text-align-center"></ui5-icon>
177+
<ui5-icon name="text-align-left"></ui5-icon>
178+
<ui5-icon name="text-align-right"></ui5-icon>
179+
<ui5-icon name="text-formatting"></ui5-icon>
180+
<ui5-icon name="line-chart-time-axis"></ui5-icon>
181+
<br>
182+
<ui5-checkbox checked></ui5-checkbox>
183+
</section>
184+
185+
<section class="sectionLTR2" dir="ltr">
186+
<h3>Icons in LTR</h3>
187+
<ui5-icon name="slim-arrow-left"></ui5-icon>
188+
<ui5-icon name="slim-arrow-right"></ui5-icon>
189+
<ui5-icon name="media-play"></ui5-icon>
190+
<ui5-icon name="media-reverse"></ui5-icon>
191+
<ui5-icon name="nav-back"></ui5-icon>
192+
<ui5-icon name="trend-down"></ui5-icon>
193+
<ui5-icon name="trend-up"></ui5-icon>
194+
<ui5-icon name="undo"></ui5-icon>
195+
<ui5-icon name="step"></ui5-icon>
196+
</section>
197+
198+
<section class="sectionRTLMirrored" dir="rtl">
199+
<h3>Icons in RTL and mirrored</h3>
200+
<ui5-icon name="slim-arrow-left"></ui5-icon>
201+
<ui5-icon name="slim-arrow-right"></ui5-icon>
202+
<ui5-icon name="media-play"></ui5-icon>
203+
<ui5-icon name="media-reverse"></ui5-icon>
204+
<ui5-icon name="nav-back"></ui5-icon>
205+
<ui5-icon name="trend-down"></ui5-icon>
206+
<ui5-icon name="trend-up"></ui5-icon>
207+
<ui5-icon name="undo"></ui5-icon>
208+
<ui5-icon name="step"></ui5-icon>
209+
</section>
133210
</body>
134211
</html>

packages/tools/lib/create-icons/index.js

+13-13
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,39 @@ const destDir = `dist/icons/`;
77

88
mkdirp.sync(destDir);
99

10-
const template = (name, pathData) => `import { registerIcon } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
10+
const template = (name, pathData, ltr) => `import { registerIcon } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
1111
1212
const name = "${name}";
1313
const pathData = "${pathData}";
14+
const ltr = ${ltr};
1415
15-
registerIcon(name, { pathData });
16+
registerIcon(name, { pathData, ltr});
1617
1718
export default { pathData };`;
1819

19-
const accTemplate = (name, pathData, accData) => `import { registerIcon } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
20+
const accTemplate = (name, pathData, ltr, accData) => `import { registerIcon } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
2021
import { ${accData.key} } from "../generated/i18n/i18n-defaults.js";
2122
2223
const name = "${name}";
2324
const pathData = "${pathData}";
25+
const ltr = ${ltr};
2426
const accData = ${accData.key};
2527
26-
registerIcon(name, { pathData, accData });
28+
registerIcon(name, { pathData, ltr, accData });
2729
2830
export default { pathData, accData };`;
2931

3032

3133
const createIcons = (file) => {
3234
const json = JSON.parse(fs.readFileSync(file));
35+
3336
for (let name in json.data) {
34-
let content;
35-
const pathData = json.data[name];
36-
const accData = json.accData[name];
37-
38-
if (accData) {
39-
content = accTemplate(name, pathData, accData);
40-
} else {
41-
content = template(name, pathData);
42-
}
37+
const iconData = json.data[name];
38+
const pathData = iconData.path;
39+
const ltr = !!iconData.ltr;
40+
const acc = iconData.acc;
41+
42+
const content = acc ? accTemplate(name, pathData, ltr, acc) : template(name, pathData, ltr);
4343

4444
fs.writeFileSync(path.join(destDir, `${name}.js`), content);
4545
}

0 commit comments

Comments
 (0)