_rtl.scss 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. //
  2. // Copyright 2017 Google Inc.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. @use 'sass:list';
  23. @use 'sass:meta';
  24. @use 'sass:selector';
  25. @use '@material/theme/gss';
  26. @use '@material/theme/selector-ext';
  27. @use '@material/theme/theme';
  28. $include: true !default;
  29. /// Creates a rule that will be applied when a component is within the context
  30. /// of an RTL layout.
  31. ///
  32. /// @example - scss
  33. /// .mdc-foo {
  34. /// padding-left: 4px;
  35. ///
  36. /// @include rtl {
  37. /// padding-left: auto;
  38. /// padding-right: 4px;
  39. /// }
  40. /// }
  41. ///
  42. /// @example - css
  43. /// .mdc-foo {
  44. /// padding-left: 4px;
  45. /// }
  46. ///
  47. /// [dir="rtl"] .mdc-foo,
  48. /// .mdc-foo[dir="rtl"] {
  49. /// padding-left: auto;
  50. /// padding-right: 4px;
  51. /// }
  52. ///
  53. /// Note that this mixin works by checking for an ancestor element with
  54. /// `[dir="rtl"]`. As a result, nested `dir` values are not supported:
  55. ///
  56. /// @example - html
  57. /// <html dir="rtl">
  58. /// <!-- ... -->
  59. /// <div dir="ltr">
  60. /// <div class="mdc-foo">Styled incorrectly as RTL!</div>
  61. /// </div>
  62. /// </html>
  63. ///
  64. /// In the future, selectors such as the `:dir` pseudo-class
  65. /// (http://mdn.io/css/:dir) will help us mitigate this.
  66. ///
  67. /// @content Content to be styled in an RTL context.
  68. @mixin rtl() {
  69. @if ($include) {
  70. $dir-rtl: '[dir=rtl]';
  71. $rtl-selectors: list.join(
  72. selector.nest($dir-rtl, &),
  73. selector-ext.append-strict(&, $dir-rtl)
  74. );
  75. @at-root {
  76. #{$rtl-selectors} {
  77. /*rtl:begin:ignore*/
  78. @content;
  79. /*rtl:end:ignore*/
  80. }
  81. }
  82. }
  83. }
  84. // Takes a base box-model property name (`margin`, `border`, `padding`, etc.) along with a
  85. // default direction (`left` or `right`) and value, and emits rules which apply the given value to the
  86. // specified direction by default and the opposite direction in RTL.
  87. //
  88. // For example:
  89. //
  90. // ```scss
  91. // .mdc-foo {
  92. // @include rtl-reflexive-box(margin, left, 8px);
  93. // }
  94. // ```
  95. //
  96. // is equivalent to:
  97. //
  98. // ```scss
  99. // .mdc-foo {
  100. // margin-left: 8px;
  101. // margin-right: 0;
  102. //
  103. // @include rtl {
  104. // margin-left: 0;
  105. // margin-right: 8px;
  106. // }
  107. // }
  108. // ```
  109. //
  110. // whereas:
  111. //
  112. // ```scss
  113. // .mdc-foo {
  114. // @include rtl-reflexive-box(margin, right, 8px);
  115. // }
  116. // ```
  117. //
  118. // is equivalent to:
  119. //
  120. // ```scss
  121. // .mdc-foo {
  122. // margin-left: 0;
  123. // margin-right: 8px;
  124. //
  125. // @include rtl {
  126. // margin-left: 8px;
  127. // margin-right: 0;
  128. // }
  129. // }
  130. // ```
  131. //
  132. // You can also pass an optional 4th `$root-selector` argument which will be forwarded to `mdc-rtl`,
  133. // e.g. `@include rtl-reflexive-box(margin, left, 8px, '.mdc-component')`.
  134. //
  135. // Note that this function will always zero out the original value in an RTL context.
  136. // If you're trying to flip the values, use `mdc-rtl-reflexive-property()` instead.
  137. @mixin reflexive-box(
  138. $base-property,
  139. $default-direction,
  140. $value,
  141. $replace: null
  142. ) {
  143. @if (list.index((right, left), $default-direction) == null) {
  144. @error "Invalid default direction: '#{$default-direction}'. Please specifiy either 'right' or 'left'.";
  145. }
  146. $left-value: $value;
  147. $right-value: 0;
  148. @if ($default-direction == right) {
  149. $left-value: 0;
  150. $right-value: $value;
  151. }
  152. @include reflexive-property(
  153. $base-property,
  154. $left-value,
  155. $right-value,
  156. $replace: $replace
  157. );
  158. }
  159. // Takes a base property and emits rules that assign <base-property>-left to <left-value> and
  160. // <base-property>-right to <right-value> in a LTR context, and vice versa in a RTL context.
  161. // For example:
  162. //
  163. // ```scss
  164. // .mdc-foo {
  165. // @include rtl-reflexive-property(margin, auto, 12px);
  166. // }
  167. // ```
  168. //
  169. // is equivalent to:
  170. //
  171. // ```scss
  172. // .mdc-foo {
  173. // margin-left: auto;
  174. // margin-right: 12px;
  175. //
  176. // @include rtl {
  177. // margin-left: 12px;
  178. // margin-right: auto;
  179. // }
  180. // }
  181. // ```
  182. //
  183. // An optional 4th `$root-selector` argument can be given, which will be passed to `mdc-rtl`.
  184. @mixin reflexive-property(
  185. $base-property,
  186. $left-value,
  187. $right-value,
  188. $replace: null
  189. ) {
  190. $prop-left: #{$base-property}-left;
  191. $prop-right: #{$base-property}-right;
  192. @include reflexive(
  193. $prop-left,
  194. $left-value,
  195. $prop-right,
  196. $right-value,
  197. $replace: $replace
  198. );
  199. }
  200. // Takes an argument specifying a horizontal position property (either 'left' or 'right') as well
  201. // as a value, and applies that value to the specified position in a LTR context, and flips it in a
  202. // RTL context. For example:
  203. //
  204. // ```scss
  205. // .mdc-foo {
  206. // @include rtl-reflexive-position(left, 0);
  207. // }
  208. // ```
  209. //
  210. // is equivalent to:
  211. //
  212. // ```scss
  213. // .mdc-foo {
  214. // left: 0;
  215. // right: initial;
  216. //
  217. // @include rtl {
  218. // left: initial;
  219. // right: 0;
  220. // }
  221. // }
  222. // ```
  223. //
  224. // An optional third $root-selector argument may also be given, which is passed to `mdc-rtl`.
  225. @mixin reflexive-position($position-property, $value, $replace: null) {
  226. @if (list.index((right, left), $position-property) == null) {
  227. @error "Invalid position #{position-property}. Please specifiy either right or left";
  228. }
  229. // TODO: 'initial' is not supported in IE 11. https://caniuse.com/#feat=css-initial-value
  230. $left-value: $value;
  231. $right-value: initial;
  232. @if ($position-property == right) {
  233. $right-value: $value;
  234. $left-value: initial;
  235. }
  236. @include reflexive(
  237. left,
  238. $left-value,
  239. right,
  240. $right-value,
  241. $replace: $replace
  242. );
  243. }
  244. // Takes pair of properties with values as arguments and flips it in RTL context.
  245. // For example:
  246. //
  247. // ```scss
  248. // .mdc-foo {
  249. // @include rtl-reflexive(left, 2px, right, 5px);
  250. // }
  251. // ```
  252. //
  253. // is equivalent to:
  254. //
  255. // ```scss
  256. // .mdc-foo {
  257. // left: 2px;
  258. // right: 5px;
  259. //
  260. // @include rtl {
  261. // right: 2px;
  262. // left: 5px;
  263. // }
  264. // }
  265. // ```
  266. //
  267. // An optional fifth `$root-selector` argument may also be given, which is passed to `mdc-rtl`.
  268. @mixin reflexive(
  269. $left-property,
  270. $left-value,
  271. $right-property,
  272. $right-value,
  273. $replace: null
  274. ) {
  275. $left-replace: null;
  276. $right-replace: null;
  277. @if $replace {
  278. @if meta.type-of($left-value) == 'string' {
  279. $left-replace: $replace;
  280. }
  281. @if meta.type-of($right-value) == 'string' {
  282. $right-replace: $replace;
  283. }
  284. @if $left-replace == null and $right-replace == null {
  285. @error 'mdc-rtl: $replace may only be used with strings but neither left nor right values are strings.';
  286. }
  287. // If any replacements are null, treat the entire value as null (do not
  288. // emit anything).
  289. @each $name, $replacement in $replace {
  290. @if $replacement == null {
  291. $left-value: null;
  292. $right-value: null;
  293. }
  294. }
  295. }
  296. // Do not emit if either value are null
  297. @if $left-value and $right-value {
  298. @include _property($left-property, $left-value, $replace: $left-replace);
  299. @include _property($right-property, $right-value, $replace: $right-replace);
  300. @include rtl {
  301. @include _property(
  302. $left-property,
  303. $right-value,
  304. $replace: $right-replace
  305. );
  306. @include _property($right-property, $left-value, $replace: $left-replace);
  307. }
  308. }
  309. }
  310. ///
  311. /// Adds RTL ignore annotation when `$mdc-rtl-include` is true.
  312. ///
  313. @mixin ignore-next-line() {
  314. @include gss.annotate(
  315. (
  316. noflip: $include,
  317. )
  318. );
  319. }
  320. ///
  321. /// Adds `@noflip` annotation when `$mdc-rtl-include` is true.
  322. ///
  323. /// @param {String} $property
  324. /// @param {String} $value
  325. /// @param {Map} $replace
  326. ///
  327. @mixin _property($property, $value, $replace: null) {
  328. @include theme.property(
  329. $property,
  330. $value,
  331. $replace: $replace,
  332. $gss: (noflip: $include)
  333. );
  334. }