From b8fc7c7d17e0450126089811cb917c5fa653eedb Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Thu, 26 Mar 2020 17:40:45 +0200 Subject: [PATCH 001/184] Add eslint-plugin-compat --- .eslintrc.json | 1 + build/.eslintrc.json | 1 + js/src/carousel.js | 2 ++ package-lock.json | 57 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 5 files changed, 62 insertions(+) diff --git a/.eslintrc.json b/.eslintrc.json index ec6aa589c..fd8df59ac 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,7 @@ { "root": true, "extends": [ + "plugin:compat/recommended", "plugin:import/errors", "plugin:import/warnings", "plugin:unicorn/recommended", diff --git a/build/.eslintrc.json b/build/.eslintrc.json index 679bd26f7..e0556caab 100644 --- a/build/.eslintrc.json +++ b/build/.eslintrc.json @@ -8,6 +8,7 @@ }, "extends": "../.eslintrc.json", "rules": { + "compat/compat": "off", "no-console": "off", "strict": "error" } diff --git a/js/src/carousel.js b/js/src/carousel.js index 9c6fb53ee..3fbcd9795 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -119,8 +119,10 @@ class Carousel extends BaseComponent { this._config = this._getConfig(config) this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element) + /* eslint-disable compat/compat */ this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 this._pointerEvent = Boolean(window.PointerEvent) + /* eslint-enable compat/compat */ this._addEventListeners() } diff --git a/package-lock.json b/package-lock.json index d2936dace..ec88d349a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1524,6 +1524,12 @@ "dev": true, "optional": true }, + "ast-metadata-inferer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.4.0.tgz", + "integrity": "sha512-tKHdBe8N/Vq2nLAm4YPBVREVZjMux6KrqyPfNQgIbDl0t7HaNSmy8w4OyVHYg/cvyn5BW7o7pVwpjPte89Zhcg==", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -2458,6 +2464,12 @@ "quick-lru": "^4.0.1" } }, + "caniuse-db": { + "version": "1.0.30001164", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001164.tgz", + "integrity": "sha512-IbIthrNouxc17oTlwskmg3M1wUVjobS11fBGsym6EnTBZit6vtax2iDyZiSPHCRA1BRO9pO+IDhES8YyXNbaoQ==", + "dev": true + }, "caniuse-lite": { "version": "1.0.30001164", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001164.tgz", @@ -3926,6 +3938,36 @@ "pkg-dir": "^2.0.0" } }, + "eslint-plugin-compat": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-3.8.0.tgz", + "integrity": "sha512-5CuWUSZXZkXLCQJBriEpndn/YWrvggDSHTpRJq++kR8GVcsWbTdp8Eh+nBA7JlrNi7ZJ/+kniOVXmn3bpnxuRA==", + "dev": true, + "requires": { + "ast-metadata-inferer": "^0.4.0", + "browserslist": "^4.12.2", + "caniuse-db": "^1.0.30001090", + "core-js": "^3.6.5", + "find-up": "^4.1.0", + "lodash.memoize": "4.1.2", + "mdn-browser-compat-data": "^1.0.28", + "semver": "7.3.2" + }, + "dependencies": { + "core-js": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.0.tgz", + "integrity": "sha512-W2VYNB0nwQQE7tKS7HzXd7r2y/y2SVJl4ga6oH/dnaLFzM0o2lB2P3zCkWj5Wc/zyMYjtgd5Hmhk0ObkQFZOIA==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, "eslint-plugin-import": { "version": "2.22.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", @@ -6604,6 +6646,12 @@ "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=", "dev": true }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6837,6 +6885,15 @@ "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", "dev": true }, + "mdn-browser-compat-data": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/mdn-browser-compat-data/-/mdn-browser-compat-data-1.1.2.tgz", + "integrity": "sha512-uBNX2P4iu3PZcXP20rL+n7fxN9PWZLj0y43QMe/1aXzqP3H6HbVOeePS0cBZCtMwcfr2Tugf1OHj+/wLam+dUg==", + "dev": true, + "requires": { + "extend": "3.0.2" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", diff --git a/package.json b/package.json index 7d737a1ac..5d7e4c703 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,7 @@ "cross-env": "^7.0.3", "eslint": "^7.14.0", "eslint-config-xo": "^0.33.1", + "eslint-plugin-compat": "^3.8.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-unicorn": "^23.0.0", "find-unused-sass-variables": "^3.0.0", From 81f6db3cb9724cbb347ff3589849d5350cffd1b5 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Thu, 3 Dec 2020 19:27:20 +0200 Subject: [PATCH 002/184] WIP --- js/src/carousel.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/js/src/carousel.js b/js/src/carousel.js index 3fbcd9795..d4a24ab82 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -119,10 +119,9 @@ class Carousel extends BaseComponent { this._config = this._getConfig(config) this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element) - /* eslint-disable compat/compat */ - this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 - this._pointerEvent = Boolean(window.PointerEvent) - /* eslint-enable compat/compat */ + /* eslint-disable-next-line compat/compat */ + this._touchSupported = 'ontouchstart' in window || (('maxTouchPoints' in navigator) && (navigator.maxTouchPoints > 0)) + this._pointerEvent = PointerEvent in window this._addEventListeners() } From 71ecc3323fb60ea05456470d10d17b614fe6dc04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Poupard?= Date: Mon, 8 Jun 2020 18:19:14 +0300 Subject: [PATCH 003/184] chore(RTL): add RTLCSS --- build/generate-sri.js | 4 ++++ package-lock.json | 55 +++++++++++++++++++++++++++++++++++++++++++ package.json | 9 +++++-- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/build/generate-sri.js b/build/generate-sri.js index de269b040..0c272cead 100644 --- a/build/generate-sri.js +++ b/build/generate-sri.js @@ -29,6 +29,10 @@ const files = [ file: 'dist/css/bootstrap.min.css', configPropertyName: 'css_hash' }, + { + file: 'dist/css/bootstrap.rtl.min.css', + configPropertyName: 'css_rtl_hash' + }, { file: 'dist/js/bootstrap.min.js', configPropertyName: 'js_hash' diff --git a/package-lock.json b/package-lock.json index d2936dace..3d7aab102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1050,6 +1050,23 @@ "to-fast-properties": "^2.0.0" } }, + "@choojs/findup": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz", + "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==", + "dev": true, + "requires": { + "commander": "^2.15.1" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, "@eslint/eslintrc": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", @@ -9220,6 +9237,44 @@ } } }, + "rtlcss": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-2.6.2.tgz", + "integrity": "sha512-06LFAr+GAPo+BvaynsXRfoYTJvSaWRyOhURCQ7aeI1MKph9meM222F+Zkt3bDamyHHJuGi3VPtiRkpyswmQbGA==", + "dev": true, + "requires": { + "@choojs/findup": "^0.2.1", + "chalk": "^2.4.2", + "mkdirp": "^0.5.1", + "postcss": "^6.0.23", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, "run-parallel": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", diff --git a/package.json b/package.json index 7d737a1ac..934b9fa78 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,15 @@ "scripts": { "start": "npm-run-all --parallel watch docs-serve", "bundlewatch": "bundlewatch --config .bundlewatch.config.json", - "css": "npm-run-all css-compile css-prefix css-minify", + "css": "npm-run-all css-compile css-prefix css-rtl css-minify", "css-compile": "sass --style expanded --source-map --embed-sources --no-error-css scss/:dist/css/", + "css-rtl": "postcss --use rtlcss --map false --dir \"dist/css\" --ext \".rtl.css\" \"dist/css/*.css\" \"!dist/css/*.min.css\" \"!dist/css/*.rtl.css\"", "css-lint": "npm-run-all --continue-on-error --parallel css-lint-*", "css-lint-stylelint": "stylelint \"**/*.{css,scss}\" --cache --cache-location .cache/.stylelintcache --rd", "css-lint-vars": "fusv scss/ site/assets/scss/", - "css-minify": "cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap.min.css dist/css/bootstrap.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-grid.min.css dist/css/bootstrap-grid.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-utilities.min.css dist/css/bootstrap-utilities.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-reboot.min.css dist/css/bootstrap-reboot.css", + "css-minify": "npm-run-all --parallel css-minify-*", + "css-minify-main": "cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap.min.css dist/css/bootstrap.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-grid.min.css dist/css/bootstrap-grid.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-utilities.min.css dist/css/bootstrap-utilities.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-reboot.min.css dist/css/bootstrap-reboot.css", + "css-minify-rtl": "cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap.rtl.min.css dist/css/bootstrap.rtl.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-grid.rtl.min.css dist/css/bootstrap-grid.rtl.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-utilities.rtl.min.css dist/css/bootstrap-utilities.rtl.css && cleancss -O1 --format breakWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-reboot.rtl.min.css dist/css/bootstrap-reboot.rtl.css", "css-prefix": "npm-run-all --parallel css-prefix-*", "css-prefix-main": "postcss --config build/postcss.config.js --replace \"dist/css/*.css\" \"!dist/css/*.min.css\"", "css-prefix-examples": "postcss --config build/postcss.config.js --replace \"site/content/**/*.css\"", @@ -68,6 +71,7 @@ "netlify": "cross-env-shell HUGO_BASEURL=$DEPLOY_PRIME_URL npm-run-all dist release-sri docs-build", "watch": "npm-run-all --parallel watch-*", "watch-css-main": "nodemon --watch scss/ --ext scss --exec \"npm-run-all css-lint css-compile css-prefix\"", + "watch-css-dist": "nodemon --watch dist/css/ --ext css --ignore \"dist/css/*.rtl.*\" --exec \"npm run css-rtl\"", "watch-css-docs": "nodemon --watch site/assets/scss/ --ext scss --exec \"npm run css-lint\"", "watch-js-main": "nodemon --watch js/src/ --ext js --exec \"npm-run-all js-lint js-compile\"", "watch-js-docs": "nodemon --watch site/assets/js/ --ext js --exec \"npm run js-lint\"" @@ -131,6 +135,7 @@ "postcss-cli": "^8.3.0", "rollup": "^2.34.0", "rollup-plugin-istanbul": "^2.0.1", + "rtlcss": "^2.6.2", "sass": "^1.29.0", "shelljs": "^0.8.4", "stylelint": "^13.8.0", From 9488978fb55286ba83e8193a871d1ff9815045b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Poupard?= Date: Fri, 26 Jun 2020 17:06:20 +0300 Subject: [PATCH 004/184] feat(RTL): implement RTL Using RTLCSS directives, renaming things to use logical names and following best practices. --- .bundlewatch.config.json | 4 +-- js/src/carousel.js | 8 ++--- js/src/dropdown.js | 27 +++++++-------- js/src/modal.js | 5 +-- js/src/popover.js | 2 +- js/src/tooltip.js | 19 +++++++++-- js/src/util/index.js | 5 ++- js/tests/unit/dropdown.spec.js | 20 +++++------ js/tests/unit/toast.spec.js | 4 +-- js/tests/unit/tooltip.spec.js | 34 +++++++++++++++++++ js/tests/visual/dropdown.html | 34 +++++++++---------- js/tests/visual/popover.html | 4 +-- js/tests/visual/scrollspy.html | 2 +- js/tests/visual/toast.html | 10 +++--- js/tests/visual/tooltip.html | 5 ++- scss/_breadcrumb.scss | 2 +- scss/_button-group.scss | 8 ++--- scss/_card.scss | 6 ++-- scss/_carousel.scss | 35 ++++++++++++------- scss/_dropdown.scss | 26 ++++++++------- scss/_list-group.scss | 8 ++--- scss/_navbar.scss | 2 +- scss/_pagination.scss | 2 +- scss/_popover.scss | 43 ++++++++++++------------ scss/_reboot.scss | 16 +++++++++ scss/_spinners.scss | 2 +- scss/_tooltip.scss | 24 +++++++------- scss/_utilities.scss | 59 ++++++++++++++++++++------------- scss/_variables.scss | 17 +++++----- scss/bootstrap-grid.scss | 12 +++---- scss/forms/_form-check.scss | 10 +++--- scss/forms/_input-group.scss | 6 ++-- scss/mixins/_border-radius.scss | 12 +++---- scss/mixins/_caret.scss | 12 +++---- scss/mixins/_forms.scss | 2 +- scss/mixins/_pagination.scss | 6 ++-- scss/mixins/_utilities.scss | 8 +++++ 37 files changed, 301 insertions(+), 200 deletions(-) diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 525d29f6a..8be4f8c7a 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -10,7 +10,7 @@ }, { "path": "./dist/css/bootstrap-reboot.css", - "maxSize": "2 kB" + "maxSize": "2.25 kB" }, { "path": "./dist/css/bootstrap-reboot.min.css", @@ -54,7 +54,7 @@ }, { "path": "./dist/js/bootstrap.min.js", - "maxSize": "15.5 kB" + "maxSize": "15.75 kB" } ], "ci": { diff --git a/js/src/carousel.js b/js/src/carousel.js index 9c6fb53ee..d8ad3a135 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -79,8 +79,8 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_CAROUSEL = 'carousel' const CLASS_NAME_ACTIVE = 'active' const CLASS_NAME_SLIDE = 'slide' -const CLASS_NAME_RIGHT = 'carousel-item-right' -const CLASS_NAME_LEFT = 'carousel-item-left' +const CLASS_NAME_END = 'carousel-item-end' +const CLASS_NAME_START = 'carousel-item-start' const CLASS_NAME_NEXT = 'carousel-item-next' const CLASS_NAME_PREV = 'carousel-item-prev' const CLASS_NAME_POINTER_EVENT = 'pointer-event' @@ -442,11 +442,11 @@ class Carousel extends BaseComponent { let eventDirectionName if (direction === DIRECTION_NEXT) { - directionalClassName = CLASS_NAME_LEFT + directionalClassName = CLASS_NAME_START orderClassName = CLASS_NAME_NEXT eventDirectionName = DIRECTION_LEFT } else { - directionalClassName = CLASS_NAME_RIGHT + directionalClassName = CLASS_NAME_END orderClassName = CLASS_NAME_PREV eventDirectionName = DIRECTION_RIGHT } diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 7b3bf5b4e..0ac108ab8 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -11,6 +11,7 @@ import { getElementFromSelector, isElement, isVisible, + isRTL, noop, typeCheckConfig } from './util/index' @@ -53,9 +54,9 @@ const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_DISABLED = 'disabled' const CLASS_NAME_SHOW = 'show' const CLASS_NAME_DROPUP = 'dropup' -const CLASS_NAME_DROPRIGHT = 'dropright' -const CLASS_NAME_DROPLEFT = 'dropleft' -const CLASS_NAME_MENURIGHT = 'dropdown-menu-right' +const CLASS_NAME_DROPEND = 'dropend' +const CLASS_NAME_DROPSTART = 'dropstart' +const CLASS_NAME_MENUEND = 'dropdown-menu-end' const CLASS_NAME_NAVBAR = 'navbar' const CLASS_NAME_POSITION_STATIC = 'position-static' @@ -65,12 +66,12 @@ const SELECTOR_MENU = '.dropdown-menu' const SELECTOR_NAVBAR_NAV = '.navbar-nav' const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)' -const PLACEMENT_TOP = 'top-start' -const PLACEMENT_TOPEND = 'top-end' -const PLACEMENT_BOTTOM = 'bottom-start' -const PLACEMENT_BOTTOMEND = 'bottom-end' -const PLACEMENT_RIGHT = 'right-start' -const PLACEMENT_LEFT = 'left-start' +const PLACEMENT_TOP = isRTL ? 'top-end' : 'top-start' +const PLACEMENT_TOPEND = isRTL ? 'top-start' : 'top-end' +const PLACEMENT_BOTTOM = isRTL ? 'bottom-end' : 'bottom-start' +const PLACEMENT_BOTTOMEND = isRTL ? 'bottom-start' : 'bottom-end' +const PLACEMENT_RIGHT = isRTL ? 'left-start' : 'right-start' +const PLACEMENT_LEFT = isRTL ? 'right-start' : 'left-start' const Default = { offset: 0, @@ -277,14 +278,14 @@ class Dropdown extends BaseComponent { // Handle dropup if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { - placement = this._menu.classList.contains(CLASS_NAME_MENURIGHT) ? + placement = this._menu.classList.contains(CLASS_NAME_MENUEND) ? PLACEMENT_TOPEND : PLACEMENT_TOP - } else if (parentDropdown.classList.contains(CLASS_NAME_DROPRIGHT)) { + } else if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { placement = PLACEMENT_RIGHT - } else if (parentDropdown.classList.contains(CLASS_NAME_DROPLEFT)) { + } else if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { placement = PLACEMENT_LEFT - } else if (this._menu.classList.contains(CLASS_NAME_MENURIGHT)) { + } else if (this._menu.classList.contains(CLASS_NAME_MENUEND)) { placement = PLACEMENT_BOTTOMEND } diff --git a/js/src/modal.js b/js/src/modal.js index a9cf8ae6c..94bf95f8a 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -13,6 +13,7 @@ import { getElementFromSelector, getTransitionDurationFromElement, isVisible, + isRTL, reflow, typeCheckConfig } from './util/index' @@ -435,11 +436,11 @@ class Modal extends BaseComponent { const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - if (!this._isBodyOverflowing && isModalOverflowing) { + if ((!this._isBodyOverflowing && isModalOverflowing && !isRTL) || (this._isBodyOverflowing && !isModalOverflowing && isRTL)) { this._element.style.paddingLeft = `${this._scrollbarWidth}px` } - if (this._isBodyOverflowing && !isModalOverflowing) { + if ((this._isBodyOverflowing && !isModalOverflowing && !isRTL) || (!this._isBodyOverflowing && isModalOverflowing && isRTL)) { this._element.style.paddingRight = `${this._scrollbarWidth}px` } } diff --git a/js/src/popover.js b/js/src/popover.js index 66dcb47b9..d8bd92eef 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -115,7 +115,7 @@ class Popover extends Tooltip { // Private _addAttachmentClass(attachment) { - this.getTipElement().classList.add(`${CLASS_PREFIX}-${attachment}`) + this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`) } _getContent() { diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 25599bb42..17148ed9a 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -14,6 +14,7 @@ import { getTransitionDurationFromElement, getUID, isElement, + isRTL, noop, typeCheckConfig } from './util/index' @@ -64,9 +65,9 @@ const DefaultType = { const AttachmentMap = { AUTO: 'auto', TOP: 'top', - RIGHT: 'right', + RIGHT: isRTL ? 'left' : 'right', BOTTOM: 'bottom', - LEFT: 'left' + LEFT: isRTL ? 'right' : 'left' } const Default = { @@ -453,6 +454,18 @@ class Tooltip extends BaseComponent { return title } + updateAttachment(attachment) { + if (attachment === 'right') { + return 'end' + } + + if (attachment === 'left') { + return 'start' + } + + return attachment + } + // Private _getPopperConfig(attachment) { @@ -485,7 +498,7 @@ class Tooltip extends BaseComponent { } _addAttachmentClass(attachment) { - this.getTipElement().classList.add(`${CLASS_PREFIX}-${attachment}`) + this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`) } _getOffset() { diff --git a/js/src/util/index.js b/js/src/util/index.js index 874827b16..96cadc65b 100644 --- a/js/src/util/index.js +++ b/js/src/util/index.js @@ -186,6 +186,8 @@ const onDOMContentLoaded = callback => { } } +const isRTL = document.documentElement.dir === 'rtl' + export { TRANSITION_END, getUID, @@ -201,5 +203,6 @@ export { noop, reflow, getjQuery, - onDOMContentLoaded + onDOMContentLoaded, + isRTL } diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index 145763d20..f6a5feb1b 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -227,7 +227,7 @@ describe('Dropdown', () => { fixtureEl.innerHTML = [ '