|
1 | 1 | /*
|
2 |
| - license: The MIT License, Copyright (c) 2018-2023 YUKI "Piro" Hiroshi |
| 2 | + license: The MIT License, Copyright (c) 2018-2025 YUKI "Piro" Hiroshi |
3 | 3 | original:
|
4 | 4 | https://github.com/piroor/webextensions-lib-menu-ui
|
5 | 5 | */
|
|
53 | 53 | return array;
|
54 | 54 | }
|
55 | 55 |
|
| 56 | + RTL_LANGUAGES = new Set([ |
| 57 | + 'ar', |
| 58 | + 'he', |
| 59 | + 'fa', |
| 60 | + 'ur', |
| 61 | + ]); |
| 62 | + |
| 63 | + get isRTL() { |
| 64 | + const lang = ( |
| 65 | + navigator.language || |
| 66 | + navigator.userLanguage || |
| 67 | + //(new Intl.DateTimeFormat()).resolvedOptions().locale || |
| 68 | + '' |
| 69 | + ).split('-')[0]; |
| 70 | + return this.RTL_LANGUAGES.has(lang); |
| 71 | + } |
| 72 | + |
56 | 73 | constructor(params = {}) {
|
57 | 74 | this.$lastHoverItem = null;
|
58 | 75 | this.$lastFocusedItem = null;
|
|
79 | 96 | this.$onTransitionEnd = this.$onTransitionEnd.bind(this);
|
80 | 97 | this.$onContextMenu = this.$onContextMenu.bind(this);
|
81 | 98 |
|
| 99 | + this.root.classList.toggle('rtl', this.isRTL); |
| 100 | + |
82 | 101 | if (!this.root.id)
|
83 | 102 | this.root.id = `MenuUI-root-${this.$uniqueKey}-${parseInt(Math.random() * Math.pow(2, 16))}`;
|
84 | 103 |
|
|
303 | 322 | this.$marker.style.top = `calc(${top}px + ${menuRect.height}px - 0.6em)`;
|
304 | 323 | }
|
305 | 324 |
|
306 |
| - if (containerRect.right - anchorRect.left >= menuRect.width) { |
307 |
| - left = anchorRect.left; |
308 |
| - this.$marker.style.left = `calc(${left}px + 0.5em)`; |
309 |
| - } |
310 |
| - else if (anchorRect.left - containerRect.left >= menuRect.width) { |
311 |
| - left = Math.max(0, anchorRect.right - menuRect.width); |
312 |
| - this.$marker.style.left = `calc(${left}px + ${menuRect.width}px - 1.5em)`; |
| 325 | + const canPlaceAtRight = containerRect.right - anchorRect.left >= menuRect.width; |
| 326 | + const canPlaceAtLeft = anchorRect.left - containerRect.left >= menuRect.width; |
| 327 | + |
| 328 | + if (canPlaceAtRight || canPlaceAtLeft) { |
| 329 | + if (this.isRTL) { |
| 330 | + if (canPlaceAtLeft) { |
| 331 | + left = Math.max(0, anchorRect.right - menuRect.width); |
| 332 | + this.$marker.style.left = `calc(${left}px + ${menuRect.width}px - 1.5em)`; |
| 333 | + } |
| 334 | + else { |
| 335 | + left = anchorRect.left; |
| 336 | + this.$marker.style.left = `calc(${left}px + 0.5em)`; |
| 337 | + } |
| 338 | + } |
| 339 | + else { |
| 340 | + if (canPlaceAtRight) { |
| 341 | + left = anchorRect.left; |
| 342 | + this.$marker.style.left = `calc(${left}px + 0.5em)`; |
| 343 | + } |
| 344 | + else { |
| 345 | + left = Math.max(0, anchorRect.right - menuRect.width); |
| 346 | + this.$marker.style.left = `calc(${left}px + ${menuRect.width}px - 1.5em)`; |
| 347 | + } |
| 348 | + } |
| 349 | + |
313 | 350 | }
|
314 | 351 | else {
|
315 | 352 | left = Math.max(0, containerRect.left - menuRect.width);
|
|
320 | 357 | let parentRect;
|
321 | 358 | if (menu.parentNode.localName == 'li') {
|
322 | 359 | parentRect = menu.parentNode.getBoundingClientRect();
|
323 |
| - left = parentRect.right; |
| 360 | + left = this.isRTL ? parentRect.left - menuRect.width : parentRect.right; |
324 | 361 | top = parentRect.top;
|
325 | 362 | }
|
326 | 363 |
|
327 | 364 | if (left === undefined)
|
328 | 365 | left = Math.max(0, (containerRect.width - menuRect.width) / 2);
|
| 366 | + else if (this.isRTL) |
| 367 | + left -= menuRect.width; |
| 368 | + |
329 | 369 | if (top === undefined)
|
330 | 370 | top = Math.max(0, (containerRect.height - menuRect.height) / 2);
|
331 | 371 |
|
|
601 | 641 | case 'ArrowRight':
|
602 | 642 | event.stopPropagation();
|
603 | 643 | event.preventDefault();
|
604 |
| - this.$digIn(); |
| 644 | + if (this.isRTL) |
| 645 | + this.$digOut(); |
| 646 | + else |
| 647 | + this.$digIn(); |
605 | 648 | break;
|
606 | 649 |
|
607 | 650 | case 'ArrowLeft':
|
608 | 651 | event.stopPropagation();
|
609 | 652 | event.preventDefault();
|
610 |
| - this.$digOut(); |
| 653 | + if (this.isRTL) |
| 654 | + this.$digIn(); |
| 655 | + else |
| 656 | + this.$digOut(); |
611 | 657 | break;
|
612 | 658 |
|
613 | 659 | case 'Home':
|
|
858 | 904 | position: fixed;
|
859 | 905 | z-index: 999999;
|
860 | 906 | }
|
| 907 | + ${common}.menu-ui.rtl { |
| 908 | + direction: rtl; |
| 909 | + } |
861 | 910 |
|
862 | 911 | ${common}.menu-ui.open,
|
863 | 912 | ${common}.menu-ui.open li.open > ul {
|
|
902 | 951 | ${common}.menu-ui li.has-submenu::after {
|
903 | 952 | content: "❯";
|
904 | 953 | position: absolute;
|
905 |
| - right: 0.25em; |
906 | 954 | transform: scale(0.75);
|
907 | 955 | }
|
| 956 | + ${common}.menu-ui:not(.rtl) li.has-submenu::after { |
| 957 | + right: 0.25em; |
| 958 | + } |
| 959 | + ${common}.menu-ui.rtl li.has-submenu::after { |
| 960 | + left: 0.25em; |
| 961 | + } |
908 | 962 |
|
909 | 963 | ${common}.menu-ui .accesskey {
|
910 | 964 | text-decoration: underline;
|
|
967 | 1021 | ${common}.menu-ui.menu li[data-icon],
|
968 | 1022 | ${common}.menu-ui.panel li[data-icon] {
|
969 | 1023 | --icon-size: 16px;
|
970 |
| - background-position: left center; |
971 | 1024 | background-repeat: no-repeat;
|
972 | 1025 | background-size: var(--icon-size);
|
973 |
| - padding-left: calc(var(--icon-size) + 0.7em); |
| 1026 | + padding-inline-start: calc(var(--icon-size) + 0.7em); |
| 1027 | + } |
| 1028 | + ${common}.menu-ui:not(.rtl) li[data-icon], |
| 1029 | + ${common}.menu-ui:not(.rtl).menu li[data-icon], |
| 1030 | + ${common}.menu-ui:not(.rtl).panel li[data-icon] { |
| 1031 | + background-position: left center; |
| 1032 | + } |
| 1033 | + ${common}.menu-ui.rtl li[data-icon], |
| 1034 | + ${common}.menu-ui.rtl.menu li[data-icon], |
| 1035 | + ${common}.menu-ui.rtl.panel li[data-icon] { |
| 1036 | + background-position: right center; |
974 | 1037 | }
|
975 | 1038 |
|
976 | 1039 | ${common}.menu-ui li.checkbox,
|
|
979 | 1042 | ${common}.menu-ui.panel li.checkbox,
|
980 | 1043 | ${common}.menu-ui.menu li.radio,
|
981 | 1044 | ${common}.menu-ui.panel li.radio {
|
982 |
| - padding-left: 1.7em; |
| 1045 | + padding-inline-start: 1.7em; |
983 | 1046 | }
|
984 | 1047 |
|
985 | 1048 | /* panel-like appearance */
|
|
1049 | 1112 | ${common}.menu-ui li[data-icon][data-icon-color] .icon {
|
1050 | 1113 | display: inline-block;
|
1051 | 1114 | height: var(--icon-size);
|
1052 |
| - left: 0.5em; |
1053 | 1115 | max-height: var(--icon-size);
|
1054 | 1116 | max-width: var(--icon-size);
|
1055 | 1117 | position: absolute;
|
1056 | 1118 | width: var(--icon-size);
|
1057 | 1119 | }
|
| 1120 | + ${common}.menu-ui:not(.rtl) li[data-icon][data-icon-color] .icon { |
| 1121 | + left: 0.5em; |
| 1122 | + } |
| 1123 | + ${common}.menu-ui.rtl li[data-icon][data-icon-color] .icon { |
| 1124 | + right: 0.5em; |
| 1125 | + } |
1058 | 1126 | `;
|
1059 | 1127 | document.head.appendChild(this.style);
|
1060 | 1128 | }
|
|
0 commit comments