UNPKG

13.5 kBSCSSView Raw
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
23// stylelint-disable selector-class-pattern --
24// Selector '.mdc-*' should only be used in this project.
25
26@use 'sass:math';
27@use '@material/animation/animation';
28@use '@material/dom/mixins' as dom-mixins;
29@use '@material/elevation/mixins' as elevation-mixins;
30@use '@material/feature-targeting/feature-targeting';
31@use '@material/ripple/ripple';
32@use '@material/ripple/ripple-theme';
33@use '@material/rtl/rtl';
34@use '@material/theme/css';
35@use '@material/theme/theme-color';
36@use '@material/typography/typography';
37@use './slider-theme';
38
39// Thumb variables.
40$_thumb-ripple-size: 48px;
41$_thumb-size: 20px;
42$_value-indicator-caret-width: 6px;
43
44// Track variables.
45$_track-active-height: 6px;
46$_track-inactive-height: 4px;
47
48/// Core styles for slider component.
49@mixin core-styles($query: feature-targeting.all()) {
50 @include ripple($query: $query);
51 @include without-ripple($query: $query);
52}
53
54// This API is intended for use by frameworks that may want to separate the
55// ripple-related styles from the other slider styles.
56// It is recommended that most users use `core-styles` instead.
57@mixin without-ripple($query: feature-targeting.all()) {
58 $feat-animation: feature-targeting.create-target($query, animation);
59 $feat-color: feature-targeting.create-target($query, color);
60 $feat-structure: feature-targeting.create-target($query, structure);
61
62 .mdc-slider {
63 @include _track($query: $query);
64 @include _thumb($query: $query);
65 @include _tick-marks($query: $query);
66
67 @include feature-targeting.targets($feat-structure) {
68 cursor: pointer;
69 height: $_thumb-ripple-size;
70 margin: 0 math.div($_thumb-ripple-size, 2);
71 position: relative;
72 touch-action: pan-y;
73 }
74
75 &--discrete {
76 .mdc-slider__thumb,
77 .mdc-slider__track--active_fill {
78 @include feature-targeting.targets($feat-animation) {
79 transition: transform 80ms ease;
80 }
81 }
82
83 @media (prefers-reduced-motion) {
84 .mdc-slider__thumb,
85 .mdc-slider__track--active_fill {
86 @include feature-targeting.targets($feat-animation) {
87 transition: none;
88 }
89 }
90 }
91 }
92 }
93
94 .mdc-slider--disabled {
95 @include _disabled($query: $query);
96 }
97
98 .mdc-slider__input {
99 @include feature-targeting.targets($feat-structure) {
100 cursor: pointer;
101 left: 0;
102 margin: 0;
103 height: 100%;
104 opacity: 0;
105 pointer-events: none;
106 position: absolute;
107 top: 0;
108 width: 100%;
109 }
110 }
111}
112
113// This API is intended for use by frameworks that may want to separate the
114// ripple-related styles from the other slider styles.
115// It is recommended that most users use `core-styles` instead.
116@mixin ripple($query: feature-targeting.all()) {
117 @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE
118
119 .mdc-slider__thumb {
120 @include ripple.surface($query: $query);
121 @include ripple.radius-unbounded($query: $query);
122 @include ripple-theme.states($color: slider-theme.$color, $query: $query);
123 }
124}
125
126@mixin _track($query: feature-targeting.all()) {
127 $feat-structure: feature-targeting.create-target($query, structure);
128
129 .mdc-slider__track {
130 @include feature-targeting.targets($feat-structure) {
131 height: $_track-inactive-height;
132 position: absolute;
133 top: 50%;
134 transform: translateY(-50%);
135 width: 100%;
136 }
137 }
138
139 .mdc-slider__track--active,
140 .mdc-slider__track--inactive {
141 @include feature-targeting.targets($feat-structure) {
142 display: flex;
143 height: 100%;
144 position: absolute;
145 width: 100%;
146 }
147 }
148
149 .mdc-slider__track--active {
150 @include feature-targeting.targets($feat-structure) {
151 // Set border-radius on the outer `track--active` element, and apply
152 // transform: scale(...) to the inner `track--active_fill` element,
153 // such that the track grows/shrinks as needed, but the border-radius
154 // is not affected by the scaling.
155 border-radius: 3px;
156 height: $_track-active-height;
157 overflow: hidden;
158 top: math.div($_track-inactive-height - $_track-active-height, 2);
159 }
160 }
161
162 .mdc-slider__track--active_fill {
163 @include feature-targeting.targets($feat-structure) {
164 // Use border rather than background-color to fill track, for HCM.
165 border-top: $_track-active-height solid;
166 box-sizing: border-box;
167 height: 100%;
168 width: 100%;
169 position: relative;
170 @include rtl.ignore-next-line();
171 -webkit-transform-origin: left;
172 @include rtl.ignore-next-line();
173 transform-origin: left;
174
175 @include rtl.rtl {
176 @include rtl.ignore-next-line();
177 -webkit-transform-origin: right;
178 @include rtl.ignore-next-line();
179 transform-origin: right;
180 }
181 }
182 }
183
184 .mdc-slider__track--inactive {
185 &::before {
186 @include dom-mixins.transparent-border($query: $query); // For HCM.
187 }
188
189 @include feature-targeting.targets($feat-structure) {
190 border-radius: 2px;
191 height: $_track-inactive-height;
192 left: 0;
193 top: 0;
194 }
195 }
196
197 @include slider-theme.track-active-color(
198 $color-or-map: (
199 default: slider-theme.$color,
200 disabled: slider-theme.$disabled-color,
201 ),
202 $query: $query
203 );
204 @include slider-theme.track-inactive-color(
205 $color-or-map: (
206 default: slider-theme.$color,
207 disabled: slider-theme.$disabled-color,
208 ),
209 $query: $query
210 );
211}
212
213@mixin _thumb($query: feature-targeting.all()) {
214 $feat-color: feature-targeting.create-target($query, color);
215 $feat-structure: feature-targeting.create-target($query, structure);
216
217 @include _value-indicator($query: $query);
218
219 .mdc-slider__thumb {
220 @include feature-targeting.targets($feat-structure) {
221 display: flex;
222 height: $_thumb-ripple-size;
223 @include rtl.ignore-next-line();
224 left: math.div(-$_thumb-ripple-size, 2);
225 outline: none;
226 position: absolute;
227 user-select: none;
228 width: $_thumb-ripple-size;
229 }
230
231 &--top {
232 @include feature-targeting.targets($feat-structure) {
233 z-index: 1;
234 }
235 }
236
237 &--top .mdc-slider__thumb-knob,
238 &--top.mdc-slider__thumb:hover .mdc-slider__thumb-knob,
239 &--top.mdc-slider__thumb--focused .mdc-slider__thumb-knob {
240 @include feature-targeting.targets($feat-structure) {
241 border-style: solid;
242 border-width: 1px;
243 box-sizing: content-box;
244 }
245 }
246 }
247
248 .mdc-slider__thumb-knob {
249 @include elevation-mixins.elevation($z-value: 1, $query: $query);
250
251 @include feature-targeting.targets($feat-structure) {
252 // Use border rather than background-color to fill thumb, for HCM.
253 border: math.div($_thumb-size, 2) solid;
254 border-radius: 50%;
255 box-sizing: border-box;
256 height: $_thumb-size;
257 @include rtl.ignore-next-line();
258 left: 50%;
259 position: absolute;
260 top: 50%;
261 transform: translate(-50%, -50%);
262 width: $_thumb-size;
263 }
264 }
265
266 @include slider-theme.thumb-color(
267 $color-or-map: (
268 default: slider-theme.$color,
269 disabled: slider-theme.$disabled-color,
270 ),
271 $query: $query
272 );
273 @include slider-theme.thumb-ripple-color(
274 $color: slider-theme.$color,
275 $query: $query
276 );
277}
278
279@mixin _tick-marks($query: feature-targeting.all()) {
280 $feat-structure: feature-targeting.create-target($query, structure);
281 $feat-color: feature-targeting.create-target($query, color);
282
283 .mdc-slider__tick-marks {
284 @include feature-targeting.targets($feat-structure) {
285 align-items: center;
286 box-sizing: border-box;
287 display: flex;
288 height: 100%;
289 justify-content: space-between;
290 padding: 0 1px;
291 position: absolute;
292 width: 100%;
293 }
294 }
295
296 .mdc-slider__tick-mark--active,
297 .mdc-slider__tick-mark--inactive {
298 @include feature-targeting.targets($feat-structure) {
299 border-radius: 50%;
300 height: 2px;
301 width: 2px;
302 }
303 }
304
305 @include slider-theme.tick-mark-active-color(
306 $color-or-map: (
307 default: slider-theme.$tick-mark-active-color,
308 disabled: on-primary,
309 ),
310 $query: $query
311 );
312 @include slider-theme.tick-mark-inactive-color(
313 $color-or-map: (
314 default: slider-theme.$tick-mark-inactive-color,
315 disabled: slider-theme.$disabled-color,
316 ),
317 $query: $query
318 );
319}
320
321@mixin _value-indicator($query: feature-targeting.all()) {
322 $feat-animation: feature-targeting.create-target($query, animation);
323 $feat-structure: feature-targeting.create-target($query, structure);
324
325 .mdc-slider__value-indicator-container {
326 @include feature-targeting.targets($feat-structure) {
327 bottom: math.div($_thumb-ripple-size, 2) + math.div($_thumb-size, 2) +
328 $_value-indicator-caret-width + 4px;
329 @include css.declaration(
330 left,
331 var(--slider-value-indicator-container-left, 50%),
332 50%,
333 $gss: (noflip: true)
334 );
335 pointer-events: none;
336 position: absolute;
337 @include css.declaration(
338 right,
339 var(--slider-value-indicator-container-right),
340 $gss: (noflip: true)
341 );
342 @include css.declaration(
343 transform,
344 var(--slider-value-indicator-container-transform, translateX(-50%)),
345 translateX(-50%)
346 );
347 }
348 }
349
350 .mdc-slider__value-indicator {
351 @include feature-targeting.targets($feat-animation) {
352 transition: animation.exit-permanent(transform, 100ms);
353 }
354
355 @include feature-targeting.targets($feat-structure) {
356 align-items: center;
357 border-radius: 4px;
358 display: flex;
359 height: 32px;
360 padding: 0 12px;
361 transform: scale(0);
362 transform-origin: bottom;
363 }
364
365 // Caret: https://css-tricks.com/snippets/css/css-triangle/
366 &::before {
367 @include feature-targeting.targets($feat-structure) {
368 border-left: $_value-indicator-caret-width solid transparent;
369 border-right: $_value-indicator-caret-width solid transparent;
370 border-top: $_value-indicator-caret-width solid;
371 bottom: -$_value-indicator-caret-width + 1;
372 content: '';
373 height: 0;
374 @include css.declaration(
375 left,
376 var(--slider-value-indicator-caret-left, 50%),
377 50%,
378 $gss: (noflip: true)
379 );
380 position: absolute;
381 @include css.declaration(
382 right,
383 var(--slider-value-indicator-caret-right),
384 $gss: (noflip: true)
385 );
386 @include css.declaration(
387 transform,
388 var(--slider-value-indicator-caret-transform, translateX(-50%)),
389 translateX(-50%)
390 );
391 width: 0;
392 }
393 }
394
395 &::after {
396 @include dom-mixins.transparent-border($query: $query); // For HCM.
397 }
398 }
399
400 .mdc-slider__thumb--with-indicator {
401 .mdc-slider__value-indicator-container {
402 @include feature-targeting.targets($feat-structure) {
403 pointer-events: auto;
404 }
405 }
406
407 .mdc-slider__value-indicator {
408 @include feature-targeting.targets($feat-animation) {
409 transition: animation.enter(transform, 100ms);
410 }
411
412 @include feature-targeting.targets($feat-structure) {
413 transform: scale(1);
414 }
415 }
416 }
417
418 @media (prefers-reduced-motion) {
419 .mdc-slider__value-indicator,
420 .mdc-slider__thumb--with-indicator .mdc-slider__value-indicator {
421 @include feature-targeting.targets($feat-animation) {
422 transition: none;
423 }
424 }
425 }
426
427 .mdc-slider__value-indicator-text {
428 @include typography.typography(subtitle2, $query);
429 }
430
431 @include slider-theme.value-indicator-color(
432 $color: slider-theme.$value-indicator-color,
433 $opacity: slider-theme.$value-indicator-opacity,
434 $query: $query
435 );
436 @include slider-theme.value-indicator-text-color(
437 $color: slider-theme.$value-indicator-text-color,
438 $query: $query
439 );
440}
441
442// Styles for slider in disabled state.
443@mixin _disabled($query: feature-targeting.all()) {
444 $feat-color: feature-targeting.create-target($query, color);
445 $feat-structure: feature-targeting.create-target($query, structure);
446
447 @include feature-targeting.targets($feat-color) {
448 opacity: 0.38;
449 }
450
451 @include feature-targeting.targets($feat-structure) {
452 cursor: auto;
453 }
454
455 .mdc-slider__thumb {
456 @include feature-targeting.targets($feat-structure) {
457 pointer-events: none;
458 }
459 }
460}