_slider.scss 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. //
  2. // Copyright 2020 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 '@material/animation/animation';
  26. @use '@material/dom/mixins' as dom-mixins;
  27. @use '@material/feature-targeting/feature-targeting';
  28. @use '@material/ripple/ripple';
  29. @use '@material/ripple/ripple-theme';
  30. @use '@material/rtl/rtl';
  31. @use '@material/theme/css';
  32. @use '@material/theme/theme-color';
  33. @use '@material/typography/typography';
  34. @use './slider-theme';
  35. // Thumb variables.
  36. $_thumb-ripple-size: 48px;
  37. $_thumb-size: 20px;
  38. $_value-indicator-caret-width: 6px;
  39. // Track variables.
  40. $_track-active-height: 6px;
  41. $_track-inactive-height: 4px;
  42. /// Core styles for slider component.
  43. @mixin core-styles($query: feature-targeting.all()) {
  44. @include ripple($query: $query);
  45. @include without-ripple($query: $query);
  46. }
  47. // This API is intended for use by frameworks that may want to separate the
  48. // ripple-related styles from the other slider styles.
  49. // It is recommended that most users use `core-styles` instead.
  50. @mixin without-ripple($query: feature-targeting.all()) {
  51. $feat-structure: feature-targeting.create-target($query, structure);
  52. @include static-styles($query: $query);
  53. .mdc-slider {
  54. @include slider-theme.theme-styles(
  55. slider-theme.$light-theme,
  56. $query: $query
  57. );
  58. }
  59. }
  60. // This API is intended for use by frameworks that may want to separate the
  61. // ripple-related styles from the other slider styles.
  62. // It is recommended that most users use `core-styles` instead.
  63. @mixin ripple($query: feature-targeting.all()) {
  64. @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE
  65. .mdc-slider__thumb {
  66. @include ripple.surface($query: $query);
  67. @include ripple.radius-unbounded($query: $query);
  68. @include ripple-theme.states($color: slider-theme.$color, $query: $query);
  69. }
  70. }
  71. @mixin static-styles($query: feature-targeting.all()) {
  72. $feat-animation: feature-targeting.create-target($query, animation);
  73. $feat-color: feature-targeting.create-target($query, color);
  74. $feat-structure: feature-targeting.create-target($query, structure);
  75. .mdc-slider {
  76. @include _track($query: $query);
  77. @include _thumb($query: $query);
  78. @include _tick-marks($query: $query);
  79. @include feature-targeting.targets($feat-structure) {
  80. cursor: pointer;
  81. height: $_thumb-ripple-size;
  82. margin: 0 math.div($_thumb-ripple-size, 2);
  83. position: relative;
  84. touch-action: pan-y;
  85. }
  86. &--discrete {
  87. .mdc-slider__thumb,
  88. .mdc-slider__track--active_fill {
  89. @include feature-targeting.targets($feat-animation) {
  90. transition: transform 80ms ease;
  91. }
  92. }
  93. @media (prefers-reduced-motion) {
  94. .mdc-slider__thumb,
  95. .mdc-slider__track--active_fill {
  96. @include feature-targeting.targets($feat-animation) {
  97. transition: none;
  98. }
  99. }
  100. }
  101. }
  102. }
  103. .mdc-slider--disabled {
  104. @include _disabled($query: $query);
  105. }
  106. .mdc-slider__input {
  107. $indent: 2px;
  108. $size: $_thumb-ripple-size - 2 * $indent;
  109. @include feature-targeting.targets($feat-structure) {
  110. cursor: pointer;
  111. left: $indent;
  112. margin: 0;
  113. height: $size;
  114. opacity: 0;
  115. pointer-events: none;
  116. position: absolute;
  117. top: $indent;
  118. width: $size;
  119. }
  120. }
  121. }
  122. @mixin _track($query: feature-targeting.all()) {
  123. $feat-structure: feature-targeting.create-target($query, structure);
  124. .mdc-slider__track {
  125. @include feature-targeting.targets($feat-structure) {
  126. position: absolute;
  127. top: 50%;
  128. transform: translateY(-50%);
  129. width: 100%;
  130. }
  131. }
  132. .mdc-slider__track--active,
  133. .mdc-slider__track--inactive {
  134. @include feature-targeting.targets($feat-structure) {
  135. display: flex;
  136. height: 100%;
  137. position: absolute;
  138. width: 100%;
  139. }
  140. }
  141. .mdc-slider__track--active {
  142. @include feature-targeting.targets($feat-structure) {
  143. overflow: hidden;
  144. }
  145. }
  146. .mdc-slider__track--active_fill {
  147. @include feature-targeting.targets($feat-structure) {
  148. // Use border rather than background-color to fill track, for HCM.
  149. border-top-style: solid;
  150. box-sizing: border-box;
  151. height: 100%;
  152. width: 100%;
  153. position: relative;
  154. @include rtl.ignore-next-line();
  155. -webkit-transform-origin: left;
  156. @include rtl.ignore-next-line();
  157. transform-origin: left;
  158. @include rtl.rtl {
  159. @include rtl.ignore-next-line();
  160. -webkit-transform-origin: right;
  161. @include rtl.ignore-next-line();
  162. transform-origin: right;
  163. }
  164. }
  165. }
  166. .mdc-slider__track--inactive {
  167. &::before {
  168. @include dom-mixins.transparent-border($query: $query); // For HCM.
  169. }
  170. @include feature-targeting.targets($feat-structure) {
  171. left: 0;
  172. top: 0;
  173. }
  174. }
  175. }
  176. @mixin _thumb($query: feature-targeting.all()) {
  177. $feat-color: feature-targeting.create-target($query, color);
  178. $feat-structure: feature-targeting.create-target($query, structure);
  179. @include _value-indicator($query: $query);
  180. .mdc-slider__thumb {
  181. @include feature-targeting.targets($feat-structure) {
  182. display: flex;
  183. @include rtl.ignore-next-line();
  184. left: math.div(-$_thumb-ripple-size, 2);
  185. outline: none;
  186. position: absolute;
  187. user-select: none;
  188. height: $_thumb-ripple-size;
  189. width: $_thumb-ripple-size;
  190. }
  191. &--top {
  192. @include feature-targeting.targets($feat-structure) {
  193. z-index: 1;
  194. }
  195. }
  196. &--top .mdc-slider__thumb-knob,
  197. &--top.mdc-slider__thumb:hover .mdc-slider__thumb-knob,
  198. &--top.mdc-slider__thumb--focused .mdc-slider__thumb-knob {
  199. @include feature-targeting.targets($feat-structure) {
  200. border-style: solid;
  201. border-width: 1px;
  202. box-sizing: content-box;
  203. }
  204. }
  205. }
  206. .mdc-slider__thumb-knob {
  207. @include feature-targeting.targets($feat-structure) {
  208. box-sizing: border-box;
  209. @include rtl.ignore-next-line();
  210. left: 50%;
  211. position: absolute;
  212. top: 50%;
  213. @include rtl.ignore-next-line();
  214. transform: translate(-50%, -50%);
  215. }
  216. }
  217. }
  218. @mixin _tick-marks($query: feature-targeting.all()) {
  219. $feat-structure: feature-targeting.create-target($query, structure);
  220. $feat-color: feature-targeting.create-target($query, color);
  221. .mdc-slider__tick-marks {
  222. @include feature-targeting.targets($feat-structure) {
  223. align-items: center;
  224. box-sizing: border-box;
  225. display: flex;
  226. height: 100%;
  227. justify-content: space-between;
  228. padding: 0 1px;
  229. position: absolute;
  230. width: 100%;
  231. }
  232. }
  233. }
  234. @mixin _value-indicator($query: feature-targeting.all()) {
  235. $feat-animation: feature-targeting.create-target($query, animation);
  236. $feat-structure: feature-targeting.create-target($query, structure);
  237. .mdc-slider__value-indicator-container {
  238. @include feature-targeting.targets($feat-structure) {
  239. bottom: math.div($_thumb-ripple-size, 2) + math.div($_thumb-size, 2) +
  240. $_value-indicator-caret-width + 4px;
  241. @include css.declaration(
  242. left,
  243. var(--slider-value-indicator-container-left, 50%),
  244. 50%,
  245. $gss: (noflip: true)
  246. );
  247. pointer-events: none;
  248. position: absolute;
  249. @include css.declaration(
  250. right,
  251. var(--slider-value-indicator-container-right),
  252. $gss: (noflip: true)
  253. );
  254. @include css.declaration(
  255. transform,
  256. var(--slider-value-indicator-container-transform, translateX(-50%)),
  257. translateX(-50%),
  258. $gss: (noflip: true)
  259. );
  260. }
  261. }
  262. .mdc-slider__value-indicator {
  263. @include feature-targeting.targets($feat-animation) {
  264. transition: animation.exit-permanent(transform, 100ms);
  265. }
  266. @include feature-targeting.targets($feat-structure) {
  267. align-items: center;
  268. border-radius: 4px;
  269. display: flex;
  270. height: 32px;
  271. padding: 0 12px;
  272. transform: scale(0);
  273. transform-origin: bottom;
  274. }
  275. // Caret: https://css-tricks.com/snippets/css/css-triangle/
  276. &::before {
  277. @include feature-targeting.targets($feat-structure) {
  278. border-left: $_value-indicator-caret-width solid transparent;
  279. border-right: $_value-indicator-caret-width solid transparent;
  280. border-top: $_value-indicator-caret-width solid;
  281. bottom: -$_value-indicator-caret-width + 1;
  282. content: '';
  283. height: 0;
  284. @include css.declaration(
  285. left,
  286. var(--slider-value-indicator-caret-left, 50%),
  287. 50%,
  288. $gss: (noflip: true)
  289. );
  290. position: absolute;
  291. @include css.declaration(
  292. right,
  293. var(--slider-value-indicator-caret-right),
  294. $gss: (noflip: true)
  295. );
  296. @include css.declaration(
  297. transform,
  298. var(--slider-value-indicator-caret-transform, translateX(-50%)),
  299. translateX(-50%),
  300. $gss: (noflip: true)
  301. );
  302. width: 0;
  303. }
  304. }
  305. &::after {
  306. @include dom-mixins.transparent-border($query: $query); // For HCM.
  307. }
  308. }
  309. .mdc-slider__thumb--with-indicator {
  310. .mdc-slider__value-indicator-container {
  311. @include feature-targeting.targets($feat-structure) {
  312. pointer-events: auto;
  313. }
  314. }
  315. .mdc-slider__value-indicator {
  316. @include feature-targeting.targets($feat-animation) {
  317. transition: animation.enter(transform, 100ms);
  318. }
  319. @include feature-targeting.targets($feat-structure) {
  320. transform: scale(1);
  321. }
  322. }
  323. }
  324. @media (prefers-reduced-motion) {
  325. .mdc-slider__value-indicator,
  326. .mdc-slider__thumb--with-indicator .mdc-slider__value-indicator {
  327. @include feature-targeting.targets($feat-animation) {
  328. transition: none;
  329. }
  330. }
  331. }
  332. }
  333. // Styles for slider in disabled state.
  334. @mixin _disabled($query: feature-targeting.all()) {
  335. $feat-structure: feature-targeting.create-target($query, structure);
  336. @include feature-targeting.targets($feat-structure) {
  337. cursor: auto;
  338. }
  339. .mdc-slider__thumb {
  340. @include feature-targeting.targets($feat-structure) {
  341. pointer-events: none;
  342. }
  343. }
  344. }