diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index ad13f19e5c7..6fa1b725b97 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -15,6 +15,7 @@ import { createColorClasses, hostContext } from '../../utils/theme'; export class Checkbox implements ComponentInterface { private inputId = `ion-cb-${checkboxIds++}`; + private buttonEl?: HTMLElement; @Element() el!: HTMLElement; @@ -98,9 +99,16 @@ export class Checkbox implements ComponentInterface { @Listen('click') onClick() { + this.setFocus(); this.checked = !this.checked; } + private setFocus() { + if (this.buttonEl) { + this.buttonEl.focus(); + } + } + private onFocus = () => { this.ionFocus.emit(); } @@ -146,6 +154,7 @@ export class Checkbox implements ComponentInterface { onFocus={this.onFocus} onBlur={this.onBlur} disabled={this.disabled} + ref={el => this.buttonEl = el} > ]; diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index aec4c3d4388..b75a192be01 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -246,6 +246,7 @@ export class Datetime implements ComponentInterface { @Listen('click') onClick() { + this.setFocus(); this.open(); } diff --git a/core/src/components/input/input.md.vars.scss b/core/src/components/input/input.md.vars.scss index 22ee3734202..375d2dd51e2 100644 --- a/core/src/components/input/input.md.vars.scss +++ b/core/src/components/input/input.md.vars.scss @@ -8,13 +8,13 @@ $input-md-font-size: inherit !default; /// @prop - Margin top of the input -$input-md-padding-top: $item-md-padding-top !default; +$input-md-padding-top: 10px !default; /// @prop - Margin end of the input $input-md-padding-end: 0 !default; /// @prop - Margin bottom of the input -$input-md-padding-bottom: $item-md-padding-bottom !default; +$input-md-padding-bottom: 10px !default; /// @prop - Margin start of the input $input-md-padding-start: ($item-md-padding-start / 2) !default; diff --git a/core/src/components/item/item.scss b/core/src/components/item/item.scss index 06ef5caec0c..c55d7002c7d 100644 --- a/core/src/components/item/item.scss +++ b/core/src/components/item/item.scss @@ -284,9 +284,8 @@ button, a { // Item Input Focused // -------------------------------------------------- -:host(.item-interactive.item-has-focus) { - --highlight-background: var(--highlight-color-focused); - +:host(.item-interactive.item-has-focus), +:host(.item-interactive.ion-touched.ion-invalid) { // If the item has a full border and highlight is enabled, show the full item highlight --full-highlight-height: #{calc(var(--highlight-height) * var(--show-full-highlight))}; @@ -294,6 +293,12 @@ button, a { --inset-highlight-height: #{calc(var(--highlight-height) * var(--show-inset-highlight))}; } +// Item Input Focus +// -------------------------------------------------- + +:host(.item-interactive.item-has-focus) { + --highlight-background: var(--highlight-color-focused); +} // Item Input Valid // -------------------------------------------------- @@ -302,7 +307,6 @@ button, a { --highlight-background: var(--highlight-color-valid); } - // Item Input Invalid // -------------------------------------------------- diff --git a/core/src/components/range/range.tsx b/core/src/components/range/range.tsx index ff1c4467176..af1e6659ac0 100644 --- a/core/src/components/range/range.tsx +++ b/core/src/components/range/range.tsx @@ -1,4 +1,4 @@ -import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, QueueApi, State, Watch } from '@stencil/core'; +import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, QueueApi, State, Watch, Listen } from '@stencil/core'; import { Color, Gesture, GestureDetail, KnobName, Mode, RangeChangeEventDetail, RangeValue, StyleEventDetail } from '../../interface'; import { clamp, debounceEvent } from '../../utils/helpers'; @@ -145,6 +145,24 @@ export class Range implements ComponentInterface { */ @Event() ionBlur!: EventEmitter; + @Listen('focusout') + onBlur() { + if (this.hasFocus) { + this.hasFocus = false; + this.ionBlur.emit(); + this.emitStyle(); + } + } + + @Listen('focusin') + onFocus() { + if (!this.hasFocus) { + this.hasFocus = true; + this.ionFocus.emit(); + this.emitStyle(); + } + } + componentWillLoad() { this.updateRatio(); this.debounceChanged(); @@ -165,7 +183,7 @@ export class Range implements ComponentInterface { this.gesture.setDisabled(this.disabled); } - private handleKeyboard = (knob: string, isIncrease: boolean) => { + private handleKeyboard = (knob: KnobName, isIncrease: boolean) => { let step = this.step; step = step > 0 ? step : 1; step = step / (this.max - this.min); @@ -200,29 +218,12 @@ export class Range implements ComponentInterface { private emitStyle() { this.ionStyle.emit({ + 'interactive': true, 'interactive-disabled': this.disabled }); } - private fireBlur() { - if (this.hasFocus) { - this.hasFocus = false; - this.ionBlur.emit(); - this.emitStyle(); - } - } - - private fireFocus() { - if (!this.hasFocus) { - this.hasFocus = true; - this.ionFocus.emit(); - this.emitStyle(); - } - } - private onStart(detail: GestureDetail) { - this.fireFocus(); - const rect = this.rect = this.rangeSlider!.getBoundingClientRect() as any; const currentX = detail.currentX; @@ -234,6 +235,8 @@ export class Range implements ComponentInterface { ? 'A' : 'B'; + this.setFocus(this.pressedKnob); + // update the active knob's position this.update(currentX); } @@ -245,7 +248,6 @@ export class Range implements ComponentInterface { private onEnd(detail: GestureDetail) { this.update(detail.currentX); this.pressedKnob = undefined; - this.fireBlur(); } private update(currentX: number) { @@ -255,8 +257,11 @@ export class Range implements ComponentInterface { let ratio = clamp(0, (currentX - rect.left) / rect.width, 1); if (this.snaps) { // snaps the ratio to the current value - const value = ratioToValue(ratio, this.min, this.max, this.step); - ratio = valueToRatio(value, this.min, this.max); + ratio = valueToRatio( + ratioToValue(ratio, this.min, this.max, this.step), + this.min, + this.max + ); } // update which knob is pressed @@ -317,6 +322,15 @@ export class Range implements ComponentInterface { this.noUpdate = false; } + private setFocus(knob: KnobName) { + if (this.el.shadowRoot) { + const knobEl = this.el.shadowRoot.querySelector(knob === 'A' ? '.range-knob-a' : '.range-knob-b') as HTMLElement | undefined; + if (knobEl) { + knobEl.focus(); + } + } + } + hostData() { return { class: { @@ -349,7 +363,10 @@ export class Range implements ComponentInterface { return [ , -
this.rangeSlider = el}> +
this.rangeSlider = el} + > {ticks.map(t => (
void; + handleKeyboard: (name: KnobName, isIncrease: boolean) => void; } function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, handleKeyboard }: RangeKnob) { @@ -431,6 +448,8 @@ function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, hand }} class={{ 'range-knob-handle': true, + 'range-knob-a': knob === 'A', + 'range-knob-b': knob === 'B', 'range-knob-pressed': pressed, 'range-knob-min': value === min, 'range-knob-max': value === max diff --git a/core/src/components/select/select.tsx b/core/src/components/select/select.tsx index 0ce45c7f736..7b9f4b704e9 100644 --- a/core/src/components/select/select.tsx +++ b/core/src/components/select/select.tsx @@ -140,6 +140,7 @@ export class Select implements ComponentInterface { @Listen('click') onClick(ev: UIEvent) { + this.setFocus(); this.open(ev); } diff --git a/core/src/components/toggle/toggle.tsx b/core/src/components/toggle/toggle.tsx index bdca2a1daa2..ec27ef0b486 100644 --- a/core/src/components/toggle/toggle.tsx +++ b/core/src/components/toggle/toggle.tsx @@ -18,6 +18,7 @@ export class Toggle implements ComponentInterface { private inputId = `ion-tg-${toggleIds++}`; private pivotX = 0; private gesture?: Gesture; + private buttonEl?: HTMLElement; @Element() el!: HTMLElement; @@ -133,6 +134,7 @@ export class Toggle implements ComponentInterface { // touch-action does not work in iOS detail.event.preventDefault(); + this.setFocus(); return true; } @@ -159,6 +161,12 @@ export class Toggle implements ComponentInterface { return this.value || ''; } + private setFocus() { + if (this.buttonEl) { + this.buttonEl.focus(); + } + } + private onFocus = () => { this.ionFocus.emit(); } @@ -205,6 +213,7 @@ export class Toggle implements ComponentInterface { onFocus={this.onFocus} onBlur={this.onBlur} disabled={this.disabled} + ref={el => this.buttonEl = el} > ];