_ripple.scss 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. //
  2. // Copyright 2016 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. // stylelint-disable selector-class-pattern --
  23. // Selector '.mdc-*' should only be used in this project.
  24. @use 'sass:math';
  25. @use 'sass:color';
  26. @use 'sass:map';
  27. @use '@material/animation/functions' as functions2;
  28. @use '@material/animation/variables' as variables2;
  29. @use '@material/base/mixins' as base-mixins;
  30. @use '@material/theme/custom-properties';
  31. @use '@material/feature-targeting/feature-targeting';
  32. @use '@material/rtl/rtl';
  33. @use '@material/theme/theme';
  34. @use './ripple-theme';
  35. @mixin core-styles($query: feature-targeting.all()) {
  36. @include static-styles($query: $query);
  37. .mdc-ripple-surface {
  38. @include ripple-theme.states($query: $query);
  39. }
  40. }
  41. @mixin static-styles($query: feature-targeting.all()) {
  42. $feat-structure: feature-targeting.create-target($query, structure);
  43. .mdc-ripple-surface {
  44. @include surface($query: $query);
  45. @include radius-bounded($query: $query);
  46. @include surface-styles($query: $query);
  47. }
  48. .mdc-ripple-surface[data-mdc-ripple-is-unbounded],
  49. .mdc-ripple-upgraded--unbounded {
  50. @include radius-unbounded($query: $query);
  51. @include unbounded-styles($query: $query);
  52. }
  53. }
  54. /// Sets all states (including hover, focus, press, activated and selected) with
  55. /// given color as base color.
  56. ///
  57. /// This mixin is for internal use only. Use `ripple-theme.states($color)` mixin
  58. /// to set interactive states (hover, focus & press) color.
  59. ///
  60. /// @param {Color|String} $color - Target base color. Can be valid CSS color or
  61. /// a color string literal (i.e., `primary`, `secondary`, etc).
  62. @mixin states-for-color($color, $query: feature-targeting.all()) {
  63. @include ripple-theme.states($color, $query: $query);
  64. @include ripple-theme.states-activated($color, $query: $query);
  65. @include ripple-theme.states-selected($color, $query: $query);
  66. }
  67. @mixin surface-styles($query: feature-targeting.all()) {
  68. $feat-structure: feature-targeting.create-target($query, structure);
  69. @include feature-targeting.targets($feat-structure) {
  70. position: relative;
  71. outline: none;
  72. overflow: hidden;
  73. }
  74. }
  75. @mixin unbounded-styles($query: feature-targeting.all()) {
  76. $feat-structure: feature-targeting.create-target($query, structure);
  77. @include feature-targeting.targets($feat-structure) {
  78. overflow: visible;
  79. }
  80. }
  81. @mixin common($query: feature-targeting.all()) {
  82. $feat-animation: feature-targeting.create-target($query, animation);
  83. // Ensure that styles needed by any component using MDC Ripple are emitted, but only once.
  84. // (Every component using MDC Ripple imports these mixins, but doesn't necessarily import
  85. // mdc-ripple.scss.)
  86. @include feature-targeting.targets($feat-animation) {
  87. @include base-mixins.emit-once('mdc-ripple/common/animation') {
  88. @include keyframes_;
  89. }
  90. }
  91. }
  92. @mixin surface(
  93. $query: feature-targeting.all(),
  94. $ripple-target: '&',
  95. $include-will-change: true // TODO(b/151931961): Remove once resolved
  96. ) {
  97. $feat-animation: feature-targeting.create-target($query, animation);
  98. $feat-structure: feature-targeting.create-target($query, structure);
  99. @include feature-targeting.targets($feat-structure) {
  100. --mdc-ripple-fg-size: 0;
  101. --mdc-ripple-left: 0;
  102. --mdc-ripple-top: 0;
  103. --mdc-ripple-fg-scale: 1;
  104. --mdc-ripple-fg-translate-end: 0;
  105. --mdc-ripple-fg-translate-start: 0;
  106. -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  107. // TODO(b/151931961): Remove the following block once resolved
  108. @if $include-will-change {
  109. will-change: transform, opacity;
  110. }
  111. }
  112. #{$ripple-target}::before,
  113. #{$ripple-target}::after {
  114. @include feature-targeting.targets($feat-structure) {
  115. position: absolute;
  116. border-radius: 50%;
  117. opacity: 0;
  118. pointer-events: none;
  119. content: '';
  120. }
  121. }
  122. #{$ripple-target}::before {
  123. @include feature-targeting.targets($feat-animation) {
  124. // Also transition background-color to avoid unnatural color flashes when toggling activated/selected state
  125. transition: opacity ripple-theme.$states-wash-duration linear,
  126. background-color ripple-theme.$states-wash-duration linear;
  127. }
  128. @include feature-targeting.targets($feat-structure) {
  129. // Ensure that the ripple wash for hover/focus states is displayed on top of positioned child elements
  130. @include theme.property(
  131. z-index,
  132. custom-properties.create(--mdc-ripple-z-index, 1)
  133. );
  134. }
  135. }
  136. #{$ripple-target}::after {
  137. @include feature-targeting.targets($feat-structure) {
  138. @include theme.property(
  139. z-index,
  140. custom-properties.create(--mdc-ripple-z-index, 0)
  141. );
  142. }
  143. }
  144. // Common styles for upgraded surfaces (some of these depend on custom properties set via JS or other mixins)
  145. &.mdc-ripple-upgraded {
  146. #{$ripple-target}::before {
  147. @include feature-targeting.targets($feat-structure) {
  148. transform: scale(var(--mdc-ripple-fg-scale, 1));
  149. }
  150. }
  151. #{$ripple-target}::after {
  152. @include feature-targeting.targets($feat-structure) {
  153. top: 0;
  154. @include rtl.ignore-next-line();
  155. left: 0;
  156. transform: scale(0);
  157. transform-origin: center center;
  158. }
  159. }
  160. }
  161. &.mdc-ripple-upgraded--unbounded {
  162. #{$ripple-target}::after {
  163. @include feature-targeting.targets($feat-structure) {
  164. top: var(--mdc-ripple-top, 0);
  165. @include rtl.ignore-next-line();
  166. left: var(--mdc-ripple-left, 0);
  167. }
  168. }
  169. }
  170. &.mdc-ripple-upgraded--foreground-activation {
  171. #{$ripple-target}::after {
  172. @include feature-targeting.targets($feat-animation) {
  173. animation: mdc-ripple-fg-radius-in ripple-theme.$translate-duration
  174. forwards,
  175. mdc-ripple-fg-opacity-in ripple-theme.$fade-in-duration forwards;
  176. }
  177. }
  178. }
  179. &.mdc-ripple-upgraded--foreground-deactivation {
  180. #{$ripple-target}::after {
  181. @include feature-targeting.targets($feat-animation) {
  182. animation: mdc-ripple-fg-opacity-out ripple-theme.$fade-out-duration;
  183. }
  184. @include feature-targeting.targets($feat-structure) {
  185. // Retain transform from mdc-ripple-fg-radius-in activation
  186. @include rtl.ignore-next-line();
  187. transform: translate(var(--mdc-ripple-fg-translate-end, 0))
  188. scale(var(--mdc-ripple-fg-scale, 1));
  189. }
  190. }
  191. }
  192. }
  193. @mixin radius-bounded(
  194. $radius: 100%,
  195. $query: feature-targeting.all(),
  196. $ripple-target: '&'
  197. ) {
  198. $feat-struture: feature-targeting.create-target($query, structure);
  199. #{$ripple-target}::before,
  200. #{$ripple-target}::after {
  201. @include feature-targeting.targets($feat-struture) {
  202. top: calc(50% - #{$radius});
  203. @include rtl.ignore-next-line();
  204. left: calc(50% - #{$radius});
  205. width: $radius * 2;
  206. height: $radius * 2;
  207. }
  208. }
  209. &.mdc-ripple-upgraded {
  210. #{$ripple-target}::after {
  211. @include feature-targeting.targets($feat-struture) {
  212. width: var(--mdc-ripple-fg-size, $radius);
  213. height: var(--mdc-ripple-fg-size, $radius);
  214. }
  215. }
  216. }
  217. }
  218. @mixin radius-unbounded(
  219. $radius: 100%,
  220. $query: feature-targeting.all(),
  221. $ripple-target: '&'
  222. ) {
  223. $feat-struture: feature-targeting.create-target($query, structure);
  224. #{$ripple-target}::before,
  225. #{$ripple-target}::after {
  226. @include feature-targeting.targets($feat-struture) {
  227. top: calc(50% - #{math.div($radius, 2)});
  228. @include rtl.ignore-next-line();
  229. left: calc(50% - #{math.div($radius, 2)});
  230. width: $radius;
  231. height: $radius;
  232. }
  233. }
  234. &.mdc-ripple-upgraded {
  235. #{$ripple-target}::before,
  236. #{$ripple-target}::after {
  237. @include feature-targeting.targets($feat-struture) {
  238. top: var(--mdc-ripple-top, calc(50% - #{math.div($radius, 2)}));
  239. @include rtl.ignore-next-line();
  240. left: var(--mdc-ripple-left, calc(50% - #{math.div($radius, 2)}));
  241. width: var(--mdc-ripple-fg-size, $radius);
  242. height: var(--mdc-ripple-fg-size, $radius);
  243. }
  244. }
  245. #{$ripple-target}::after {
  246. @include feature-targeting.targets($feat-struture) {
  247. width: var(--mdc-ripple-fg-size, $radius);
  248. height: var(--mdc-ripple-fg-size, $radius);
  249. }
  250. }
  251. }
  252. }
  253. // Common styles for a ripple target element.
  254. // Used for components which have an inner ripple target element.
  255. @mixin target-common($query: feature-targeting.all()) {
  256. $feat-structure: feature-targeting.create-target($query, structure);
  257. @include feature-targeting.targets($feat-structure) {
  258. position: absolute;
  259. top: 0;
  260. left: 0;
  261. width: 100%;
  262. height: 100%;
  263. // Necessary for clicks on other inner elements (e.g. close icon in chip)
  264. // to go through.
  265. pointer-events: none;
  266. }
  267. }
  268. @mixin keyframes_ {
  269. @keyframes mdc-ripple-fg-radius-in {
  270. from {
  271. animation-timing-function: variables2.$standard-curve-timing-function;
  272. // NOTE: For these keyframes, we do not need custom property fallbacks because they are only
  273. // used in conjunction with `.mdc-ripple-upgraded`. Since MDCRippleFoundation checks to ensure
  274. // that custom properties are supported within the browser before adding this class, we can
  275. // safely use them without a fallback.
  276. @include rtl.ignore-next-line();
  277. transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);
  278. }
  279. to {
  280. @include rtl.ignore-next-line();
  281. transform: translate(var(--mdc-ripple-fg-translate-end, 0))
  282. scale(var(--mdc-ripple-fg-scale, 1));
  283. }
  284. }
  285. @keyframes mdc-ripple-fg-opacity-in {
  286. from {
  287. animation-timing-function: linear;
  288. opacity: 0;
  289. }
  290. to {
  291. opacity: var(--mdc-ripple-fg-opacity, 0);
  292. }
  293. }
  294. @keyframes mdc-ripple-fg-opacity-out {
  295. from {
  296. animation-timing-function: linear;
  297. opacity: var(--mdc-ripple-fg-opacity, 0);
  298. }
  299. to {
  300. opacity: 0;
  301. }
  302. }
  303. }