From 72b48da7cc3bc7dc619af3f668f95d68c6c634e5 Mon Sep 17 00:00:00 2001 From: Material Web Team Date: Fri, 21 Apr 2023 06:27:47 -0700 Subject: [PATCH] fix(slider): fixes label focus and ranged handle dragging on Safari PiperOrigin-RevId: 526016986 --- slider/harness.ts | 7 +++++ slider/lib/_slider.scss | 18 ++++++++----- slider/lib/slider.ts | 59 +++++++++++++---------------------------- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/slider/harness.ts b/slider/harness.ts index bc241729e0..f5f1a112d5 100644 --- a/slider/harness.ts +++ b/slider/harness.ts @@ -70,4 +70,11 @@ export class SliderHarness extends Harness { } super.simulateStartHover(element, init); } + + protected override simulateMousePress( + element: HTMLElement, init: PointerEventInit = {}) { + super.simulateMousePress(element, init); + // advance beyond RAF, which is used by the element's pointerDown handler. + jasmine.clock().tick(1); + } } diff --git a/slider/lib/_slider.scss b/slider/lib/_slider.scss index 1733871b75..38fda57ff4 100644 --- a/slider/lib/_slider.scss +++ b/slider/lib/_slider.scss @@ -266,17 +266,18 @@ $_md-sys-shape: tokens.md-sys-shape-values(); background: var(--_disabled-handle-color); } - .input.b:focus ~ .handleContainer .handle.b > slot > .handleNub, - .input.a:focus ~ .handleContainer .handle.a > slot > .handleNub { + input.b:focus ~ .handleContainerPadded .handle.b > .handleNub, + input.a:focus ~ .handleContainerPadded .handle.a > .handleNub { background: var(--_focus-handle-color); } // prefix classes exist to overcome specificity of focus styling. - .container > .handleContainer .handle.hover > slot > .handleNub { + .container > .handleContainerPadded .handle.hover > .handleNub { background: var(--_hover-handle-color); } - .container > .handleContainer .handle.pressed > slot > .handleNub { + input.b:active ~ .handleContainerPadded .handle.b > .handleNub, + input.a:active ~ .handleContainerPadded .handle.a > .handleNub { background: var(--_pressed-handle-color); } @@ -312,9 +313,8 @@ $_md-sys-shape: tokens.md-sys-shape-values(); min-inline-size: var(--_label-container-height); min-block-size: var(--_label-container-height); background: var(--_label-container-color); - transition-property: transform; - transition-duration: map.get($_md-sys-motion, 'duration-short2'); - transition-timing-function: map.get($_md-sys-motion, 'easing-emphasized'); + transition: transform map.get($_md-sys-motion, 'duration-short2') + map.get($_md-sys-motion, 'easing-emphasized'); transform-origin: center bottom; transform: scale(0); } @@ -387,8 +387,11 @@ $_md-sys-shape: tokens.md-sys-shape-values(); ::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; + // note, this is sized to align with thumb but is 0 width so that + // fine adjustments are possible block-size: var(--_state-layer-size); inline-size: var(--_state-layer-size); + transform: scaleX(0); opacity: 0; z-index: 2; } @@ -397,6 +400,7 @@ $_md-sys-shape: tokens.md-sys-shape-values(); appearance: none; block-size: var(--_state-layer-size); inline-size: var(--_state-layer-size); + transform: scaleX(0); opacity: 0; z-index: 2; } diff --git a/slider/lib/slider.ts b/slider/lib/slider.ts index 02ff1130ef..0fb97dd794 100644 --- a/slider/lib/slider.ts +++ b/slider/lib/slider.ts @@ -201,8 +201,6 @@ export class Slider extends LitElement { // interaction targets. @state() private handleAHover = false; @state() private handleBHover = false; - @state() private handleAPressed = false; - @state() private handleBPressed = false; @state() private onTopId = 'b'; @state() private handlesOverlapping = false; @@ -323,7 +321,6 @@ export class Slider extends LitElement { focusRequested: this.focusRingARequested, showFocus: this.focusRingAShowing, hover: this.handleAHover, - pressed: this.handleAPressed, label: labelA }; @@ -334,7 +331,6 @@ export class Slider extends LitElement { focusRequested: this.focusRingBRequested, showFocus: this.focusRingBShowing, hover: this.handleBHover, - pressed: this.handleBPressed, label: labelB }; @@ -363,10 +359,7 @@ export class Slider extends LitElement { protected renderTrack() { const trackClasses = {'tickMarks': this.withTickMarks}; - return html` - -
-
`; + return html`
`; } protected renderFocusRing(visible: boolean) { @@ -379,41 +372,27 @@ export class Slider extends LitElement { `; } - protected renderHandle({ - id, - lesser, - showRipple, - focusRequested, - showFocus, - hover, - pressed, - label - }: { - id: string, - lesser: boolean, - focusRequested: boolean, - showRipple: boolean, - showFocus: boolean, - hover: boolean, - pressed: boolean, - label: string - }) { + protected renderHandle( + {id, lesser, showRipple, focusRequested, showFocus, hover, label}: { + id: string, + lesser: boolean, + focusRequested: boolean, + showRipple: boolean, + showFocus: boolean, + hover: boolean, + label: string + }) { const onTop = !this.disabled && id === this.onTopId; const isOverlapping = !this.disabled && this.handlesOverlapping; return html`
-
${when(this.withLabel, () => this.renderLabel(label))} - -
${when(showRipple, () => this.renderRipple(id))} ${when(focusRequested, () => this.renderFocusRing(showFocus))}
`; @@ -437,7 +416,6 @@ export class Slider extends LitElement { @focus=${this.handleFocus} @blur=${this.handleBlur} @pointerdown=${this.handleDown} - @pointerup=${this.handleUp} @pointerenter=${this.handleEnter} @pointermove=${this.handleMove} @pointerleave=${this.handleLeave} @@ -517,18 +495,17 @@ export class Slider extends LitElement { pointerPress(); this.ripplePointerId = e.pointerId; const isA = this.isEventOnA(e); - const isPrimaryButton = Boolean(e.buttons & 1); // Since handle moves to pointer on down and there may not be a move, // it needs to be considered hovered.. this.handleAHover = !this.disabled && isA && Boolean(this.handleA); - this.handleAPressed = isPrimaryButton && this.handleAHover; this.handleBHover = !this.disabled && !isA && Boolean(this.handleB); - this.handleBPressed = isPrimaryButton && this.handleBHover; this.updateFocusVisible(e); - } - - protected handleUp() { - this.handleAPressed = this.handleBPressed = false; + // Force Safari to focus input so the label stays displayed; note, + // Macs don't normally focus non-text type inputs. + const target = (e.target as HTMLElement); + requestAnimationFrame(() => { + target.focus(); + }); } /** @@ -568,7 +545,7 @@ export class Slider extends LitElement { } this.valueB = this.inputB.valueAsNumber; this.updateOnTop(e); - // update value only on interaction + // update value only on interaction const lower = Math.min(this.valueA, this.valueB); const upper = Math.max(this.valueA, this.valueB); this.value = this.allowRange ? [lower, upper] : this.valueB;