mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 20:19:13 +00:00
Refactor range styles to prepare for native ranges
This commit is contained in:
@@ -103,3 +103,14 @@ You can change the tooltip's content by setting the `tooltipFormatter` property
|
||||
range.tooltipFormatter = value => `Total - ${value}%`;
|
||||
</script>
|
||||
```
|
||||
|
||||
### Right-to-Left languages
|
||||
|
||||
The component adapts to right-to-left (RTL) languages as you would expect.
|
||||
|
||||
```html {.example}
|
||||
<wa-range dir="rtl"
|
||||
label="مقدار"
|
||||
hint="التحكم في مستوى صوت الأغنية الحالية."
|
||||
style="--track-color-active: var(--wa-color-brand-fill-loud)" value="10"></wa-range>
|
||||
```
|
||||
|
||||
@@ -3,23 +3,21 @@
|
||||
--thumb-gap: calc(var(--thumb-size) * 0.125);
|
||||
--thumb-shadow: initial;
|
||||
--thumb-size: calc(1rem * var(--wa-form-control-value-line-height));
|
||||
|
||||
--tooltip-offset: calc(var(--wa-tooltip-arrow-size) * 1.375);
|
||||
|
||||
--track-color-active: var(--wa-color-neutral-fill-normal);
|
||||
--track-color-inactive: var(--wa-color-neutral-fill-normal);
|
||||
--track-active-offset: 0%;
|
||||
--track-height: calc(var(--thumb-size) * 0.25);
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
.range {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: max(var(--thumb-size), var(--track-height));
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
min-height: max(var(--thumb-size), var(--track-height));
|
||||
}
|
||||
|
||||
.control {
|
||||
input[type='range'] {
|
||||
--percent: 0%;
|
||||
-webkit-appearance: none;
|
||||
border-radius: calc(var(--track-height) / 2);
|
||||
@@ -29,126 +27,113 @@
|
||||
line-height: var(--wa-form-control-height-m);
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
--dir: right;
|
||||
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
var(--track-color-inactive) 0%,
|
||||
to var(--dir),
|
||||
var(--track-color-inactive) min(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-active) min(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-active) max(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-inactive) max(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-inactive) 100%
|
||||
var(--track-color-inactive) max(var(--percent), var(--track-active-offset))
|
||||
);
|
||||
}
|
||||
|
||||
.range--rtl .control {
|
||||
background-image: linear-gradient(
|
||||
to left,
|
||||
var(--track-color-inactive) 0%,
|
||||
var(--track-color-inactive) min(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-active) min(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-active) max(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-inactive) max(var(--percent), var(--track-active-offset)),
|
||||
var(--track-color-inactive) 100%
|
||||
);
|
||||
}
|
||||
&:dir(rtl) {
|
||||
--dir: left;
|
||||
}
|
||||
|
||||
/* Webkit */
|
||||
.control::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: var(--track-height);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
}
|
||||
&::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: var(--track-height);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.control::-webkit-slider-thumb {
|
||||
width: var(--thumb-size);
|
||||
height: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--thumb-color);
|
||||
box-shadow:
|
||||
var(--thumb-shadow, 0 0 transparent),
|
||||
0 0 0 var(--thumb-gap) var(--wa-color-surface-default);
|
||||
-webkit-appearance: none;
|
||||
margin-top: calc(var(--thumb-size) / -2 + var(--track-height) / 2);
|
||||
cursor: pointer;
|
||||
}
|
||||
&::-webkit-slider-thumb {
|
||||
width: var(--thumb-size);
|
||||
height: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--thumb-color);
|
||||
box-shadow:
|
||||
var(--thumb-shadow, 0 0 transparent),
|
||||
0 0 0 var(--thumb-gap) var(--wa-color-surface-default);
|
||||
-webkit-appearance: none;
|
||||
margin-top: calc(var(--thumb-size) / -2 + var(--track-height) / 2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.control:enabled:focus-visible::-webkit-slider-thumb {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
&:enabled {
|
||||
&:focus-visible::-webkit-slider-thumb {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
|
||||
.control:enabled::-webkit-slider-thumb:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
&::-webkit-slider-thumb:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
.control::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
&::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.control::-moz-range-progress {
|
||||
background-color: var(--track-color-active);
|
||||
border-radius: 3px;
|
||||
height: var(--track-height);
|
||||
}
|
||||
&::-moz-range-progress {
|
||||
background-color: var(--track-color-active);
|
||||
border-radius: 3px;
|
||||
height: var(--track-height);
|
||||
}
|
||||
|
||||
.control::-moz-range-track {
|
||||
width: 100%;
|
||||
height: var(--track-height);
|
||||
background-color: var(--track-color-inactive);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
}
|
||||
&::-moz-range-track {
|
||||
width: 100%;
|
||||
height: var(--track-height);
|
||||
background-color: var(--track-color-inactive);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.control::-moz-range-thumb {
|
||||
height: var(--thumb-size);
|
||||
width: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--thumb-color);
|
||||
box-shadow:
|
||||
var(--thumb-shadow),
|
||||
0 0 0 var(--thumb-gap) var(--wa-color-surface-default);
|
||||
transition:
|
||||
background-color var(--wa-transition-normal) var(--wa-transition-easing),
|
||||
border-color var(--wa-transition-normal) var(--wa-transition-easing),
|
||||
box-shadow var(--wa-transition-normal) var(--wa-transition-easing),
|
||||
color var(--wa-transition-normal) var(--wa-transition-easing);
|
||||
cursor: pointer;
|
||||
}
|
||||
&::-moz-range-thumb {
|
||||
height: var(--thumb-size);
|
||||
width: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--thumb-color);
|
||||
box-shadow:
|
||||
var(--thumb-shadow),
|
||||
0 0 0 var(--thumb-gap) var(--wa-color-surface-default);
|
||||
transition-property: background-color, border-color, box-shadow, color;
|
||||
transition-duration: var(--wa-transition-normal);
|
||||
transition-timing-function: var(--wa-transition-easing);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.control:enabled:focus-visible::-moz-range-thumb {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
&:enabled {
|
||||
&:focus-visible::-moz-range-thumb {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
|
||||
.control:enabled::-moz-range-thumb:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
&::-moz-range-thumb:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
/* States */
|
||||
.control:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
/* States */
|
||||
|
||||
.control:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.control:disabled::-webkit-slider-thumb {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.control:disabled::-moz-range-thumb {
|
||||
cursor: not-allowed;
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tooltip output */
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
inset-inline-start: 0;
|
||||
|
||||
inset-block-end: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
|
||||
border-radius: var(--wa-tooltip-border-radius);
|
||||
background-color: var(--wa-tooltip-background-color);
|
||||
font-family: inherit;
|
||||
@@ -159,60 +144,43 @@
|
||||
padding: var(--wa-space-2xs) var(--wa-space-xs);
|
||||
transition: var(--wa-transition-normal) opacity;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tooltip:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
left: 50%;
|
||||
translate: calc(-1 * var(--wa-tooltip-arrow-size));
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
inset-inline-start: 50%;
|
||||
inset-block-start: 100%;
|
||||
translate: calc(-1 * var(--wa-tooltip-arrow-size));
|
||||
border-inline: var(--wa-tooltip-arrow-size) solid transparent;
|
||||
border-block-start: var(--border-block);
|
||||
}
|
||||
|
||||
.range--tooltip-visible .tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Tooltip on top */
|
||||
.range--tooltip-top .tooltip {
|
||||
bottom: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
|
||||
}
|
||||
--inset-block: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
|
||||
--border-block: var(--wa-tooltip-arrow-size) solid var(--wa-color-neutral-fill-loud);
|
||||
|
||||
.range--tooltip-top .tooltip:after {
|
||||
border-top: var(--wa-tooltip-arrow-size) solid var(--wa-color-neutral-fill-loud);
|
||||
border-left: var(--wa-tooltip-arrow-size) solid transparent;
|
||||
border-right: var(--wa-tooltip-arrow-size) solid transparent;
|
||||
top: 100%;
|
||||
@media (forced-colors: active) {
|
||||
border: solid 1px transparent;
|
||||
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Tooltip on bottom */
|
||||
.range--tooltip-bottom .tooltip {
|
||||
top: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
|
||||
}
|
||||
:host([tooltip='bottom']) .tooltip {
|
||||
inset-block-end: auto;
|
||||
inset-block-start: calc(50% + (var(--thumb-size) / 2) + var(--tooltip-offset));
|
||||
|
||||
.range--tooltip-bottom .tooltip:after {
|
||||
border-bottom: var(--wa-tooltip-arrow-size) solid var(--wa-color-neutral-fill-loud);
|
||||
border-left: var(--wa-tooltip-arrow-size) solid transparent;
|
||||
border-right: var(--wa-tooltip-arrow-size) solid transparent;
|
||||
bottom: 100%;
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.control,
|
||||
.tooltip {
|
||||
border: solid 1px transparent;
|
||||
}
|
||||
|
||||
.control::-webkit-slider-thumb {
|
||||
border: solid 1px transparent;
|
||||
}
|
||||
|
||||
.control::-moz-range-thumb {
|
||||
border: solid 1px transparent;
|
||||
}
|
||||
|
||||
.tooltip:after {
|
||||
display: none;
|
||||
&:after {
|
||||
border-block-end: var(--border-block);
|
||||
inset-block-start: auto;
|
||||
inset-block-end: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,10 @@ import styles from './range.css';
|
||||
* @event wa-invalid - Emitted when the form control has been checked for validity and its constraints aren't satisfied.
|
||||
*
|
||||
* @csspart form-control - The form control that wraps the label, input, and hint.
|
||||
* @csspart form-control-label - The label's wrapper.
|
||||
* @csspart form-control-input - The range's wrapper.
|
||||
* @csspart form-control-label - The input's label.
|
||||
* @csspart form-control-input - The input's wrapper.
|
||||
* @csspart hint - The hint's wrapper.
|
||||
* @csspart base - The component's base wrapper.
|
||||
* @csspart input - The internal `<input>` element.
|
||||
* @csspart base - The internal `<input>` element.
|
||||
* @csspart tooltip - The range's tooltip.
|
||||
*
|
||||
* @cssproperty --thumb-color - The color of the thumb.
|
||||
@@ -280,73 +279,53 @@ export default class WaRange extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
// NOTE - always bind value after min/max, otherwise it will be clamped
|
||||
return html`
|
||||
<div
|
||||
part="form-control"
|
||||
class=${classMap({
|
||||
'form-control': true,
|
||||
'form-control--medium': true, // range only has one size
|
||||
'form-control--has-label': hasLabel,
|
||||
})}
|
||||
>
|
||||
<label part="form-control-label" class="label" for="input" aria-hidden=${hasLabel ? 'false' : 'true'}>
|
||||
<slot name="label">${this.label}</slot>
|
||||
</label>
|
||||
${hasLabel
|
||||
? html`<label part="form-control-label" class="label" for="input">
|
||||
<slot name="label">${this.label}</slot>
|
||||
</label>`
|
||||
: ''}
|
||||
|
||||
<div part="form-control-input" class="form-control-input">
|
||||
<div
|
||||
part="base"
|
||||
class=${classMap({
|
||||
range: true,
|
||||
'range--disabled': this.disabled,
|
||||
'range--focused': this.hasFocus,
|
||||
'range--rtl': this.localize.dir() === 'rtl',
|
||||
'range--tooltip-visible': this.hasTooltip,
|
||||
'range--tooltip-top': this.tooltip === 'top',
|
||||
'range--tooltip-bottom': this.tooltip === 'bottom',
|
||||
})}
|
||||
@mousedown=${this.handleThumbDragStart}
|
||||
@mouseup=${this.handleThumbDragEnd}
|
||||
@touchstart=${this.handleThumbDragStart}
|
||||
@touchend=${this.handleThumbDragEnd}
|
||||
>
|
||||
<input
|
||||
part="input"
|
||||
id="input"
|
||||
class="control"
|
||||
title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}
|
||||
type="range"
|
||||
name=${ifDefined(this.name)}
|
||||
?disabled=${this.disabled}
|
||||
min=${ifDefined(this.min)}
|
||||
max=${ifDefined(this.max)}
|
||||
step=${ifDefined(this.step)}
|
||||
.value=${live(this.value.toString())}
|
||||
aria-describedby="hint"
|
||||
@change=${this.handleChange}
|
||||
@focus=${this.handleFocus}
|
||||
@input=${this.handleInput}
|
||||
@blur=${this.handleBlur}
|
||||
/>
|
||||
${this.tooltip !== 'none' && !this.disabled
|
||||
? html`
|
||||
<output part="tooltip" class="tooltip">
|
||||
${typeof this.tooltipFormatter === 'function' ? this.tooltipFormatter(this.value) : this.value}
|
||||
</output>
|
||||
`
|
||||
: ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<slot
|
||||
name="hint"
|
||||
part="hint"
|
||||
class=${classMap({
|
||||
'has-slotted': hasHint,
|
||||
})}
|
||||
aria-hidden=${hasHint ? 'false' : 'true'}
|
||||
>${this.hint}</slot
|
||||
>
|
||||
<div part="form-control-input">
|
||||
<input
|
||||
part="base"
|
||||
id="input"
|
||||
class="control"
|
||||
title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}
|
||||
type="range"
|
||||
name=${ifDefined(this.name)}
|
||||
?disabled=${this.disabled}
|
||||
min=${ifDefined(this.min)}
|
||||
max=${ifDefined(this.max)}
|
||||
step=${ifDefined(this.step)}
|
||||
.value=${live(this.value.toString())}
|
||||
aria-describedby="hint"
|
||||
@change=${this.handleChange}
|
||||
@focus=${this.handleFocus}
|
||||
@input=${this.handleInput}
|
||||
@blur=${this.handleBlur}
|
||||
@mousedown=${this.handleThumbDragStart}
|
||||
@mouseup=${this.handleThumbDragEnd}
|
||||
@touchstart=${this.handleThumbDragStart}
|
||||
@touchend=${this.handleThumbDragEnd}
|
||||
/>
|
||||
${this.tooltip !== 'none' && !this.disabled
|
||||
? html`
|
||||
<output part="tooltip" class="${classMap({ tooltip: true, visible: this.hasTooltip })}">
|
||||
${typeof this.tooltipFormatter === 'function' ? this.tooltipFormatter(this.value) : this.value}
|
||||
</output>
|
||||
`
|
||||
: ''}
|
||||
</div>
|
||||
|
||||
<slot
|
||||
name="hint"
|
||||
part="hint"
|
||||
class=${classMap({
|
||||
'has-slotted': hasHint,
|
||||
})}
|
||||
aria-hidden=${hasHint ? 'false' : 'true'}
|
||||
>${this.hint}</slot
|
||||
>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
.form-control .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Label */
|
||||
.form-control--has-label .label {
|
||||
[part~='form-control-label'] {
|
||||
display: block;
|
||||
color: var(--wa-form-control-label-color);
|
||||
font-weight: var(--wa-form-control-label-font-weight);
|
||||
line-height: var(--wa-form-control-label-line-height);
|
||||
margin-block-end: var(--wa-space-xs);
|
||||
font-size: var(--wa-font-size-m);
|
||||
|
||||
&:is(.form-control--small *) {
|
||||
font-size: var(--wa-font-size-s);
|
||||
}
|
||||
|
||||
&:is(.form-control--medium *) {
|
||||
font-size: var(--wa-font-size-m);
|
||||
}
|
||||
|
||||
&:is(.form-control--large *) {
|
||||
font-size: var(--wa-font-size-l);
|
||||
}
|
||||
}
|
||||
|
||||
:host([required]) .form-control--has-label .label::after {
|
||||
content: var(--wa-form-control-required-content);
|
||||
margin-inline-start: var(--wa-form-control-required-content-offset);
|
||||
color: var(--wa-form-control-required-content-color);
|
||||
:host([required]) &::after {
|
||||
content: var(--wa-form-control-required-content);
|
||||
margin-inline-start: var(--wa-form-control-required-content-offset);
|
||||
color: var(--wa-form-control-required-content-color);
|
||||
}
|
||||
}
|
||||
|
||||
/* Help text */
|
||||
|
||||
Reference in New Issue
Block a user