UNPKG

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