feat(item): add hover and focused states (#18606)

references #18279 references #17624
This commit is contained in:
Brandy Carney
2019-06-25 17:29:14 -04:00
committed by GitHub
parent ad00679da9
commit 8a88dd25b6
10 changed files with 139 additions and 27 deletions

View File

@ -483,12 +483,17 @@ ion-item,prop,target,string | undefined,undefined,false,false
ion-item,prop,type,"button" | "reset" | "submit",'button',false,false ion-item,prop,type,"button" | "reset" | "submit",'button',false,false
ion-item,css-prop,--background ion-item,css-prop,--background
ion-item,css-prop,--background-activated ion-item,css-prop,--background-activated
ion-item,css-prop,--background-focused
ion-item,css-prop,--background-hover
ion-item,css-prop,--border-color ion-item,css-prop,--border-color
ion-item,css-prop,--border-radius ion-item,css-prop,--border-radius
ion-item,css-prop,--border-style ion-item,css-prop,--border-style
ion-item,css-prop,--border-width ion-item,css-prop,--border-width
ion-item,css-prop,--box-shadow ion-item,css-prop,--box-shadow
ion-item,css-prop,--color ion-item,css-prop,--color
ion-item,css-prop,--color-activated
ion-item,css-prop,--color-focused
ion-item,css-prop,--color-hover
ion-item,css-prop,--detail-icon-color ion-item,css-prop,--detail-icon-color
ion-item,css-prop,--detail-icon-font-size ion-item,css-prop,--detail-icon-font-size
ion-item,css-prop,--detail-icon-opacity ion-item,css-prop,--detail-icon-opacity

View File

@ -94,7 +94,11 @@ export class Checkbox implements ComponentInterface {
} }
@Watch('disabled') @Watch('disabled')
emitStyle() { disabledChanged() {
this.emitStyle();
}
private emitStyle() {
this.ionStyle.emit({ this.ionStyle.emit({
'checkbox-checked': this.checked, 'checkbox-checked': this.checked,
'interactive-disabled': this.disabled, 'interactive-disabled': this.disabled,

View File

@ -12,7 +12,8 @@
--inner-border-width: #{0px 0px $item-ios-border-bottom-width 0px}; --inner-border-width: #{0px 0px $item-ios-border-bottom-width 0px};
--background: #{$item-ios-background}; --background: #{$item-ios-background};
--background-activated: #{$item-ios-background-activated}; --background-activated: #{$item-ios-background-activated};
--background-focused: #{$item-ios-background-activated}; --background-focused: #{$item-ios-background-focused};
--background-hover: #{$item-ios-background-hover};
--border-color: #{$item-ios-border-bottom-color}; --border-color: #{$item-ios-border-bottom-color};
--color: #{$item-ios-color}; --color: #{$item-ios-color};
--highlight-height: 0; --highlight-height: 0;
@ -23,10 +24,31 @@
font-size: $item-ios-font-size; font-size: $item-ios-font-size;
} }
// iOS Activated
// --------------------------------------------------
:host(.activated) { :host(.activated) {
--transition: none; --transition: none;
} }
:host(.ion-color.activated) .item-native {
background: current-color(shade);
color: current-color(contrast);
}
@media (any-hover: hover) {
:host(.activated.ion-activatable:hover) .item-native {
background: var(--background-activated);
color: var(--color-activated);
}
:host(.activated.ion-color.ion-activatable:hover) .item-native {
background: #{current-color(shade)};
color: #{current-color(contrast)};
}
}
// iOS Item Lines // iOS Item Lines
// -------------------------------------------------- // --------------------------------------------------

View File

@ -9,7 +9,8 @@
--min-height: #{$item-md-min-height}; --min-height: #{$item-md-min-height};
--background: #{$item-md-background}; --background: #{$item-md-background};
--background-activated: var(--background); --background-activated: var(--background);
--background-focused: #{$item-md-background-activated}; --background-focused: #{$item-md-background-focused};
--background-hover: #{$item-md-background-hover};
--border-color: #{$item-md-border-bottom-color}; --border-color: #{$item-md-border-bottom-color};
--color: #{$item-md-color}; --color: #{$item-md-color};
--transition: background-color 300ms cubic-bezier(.4, 0, .2, 1); --transition: background-color 300ms cubic-bezier(.4, 0, .2, 1);
@ -30,6 +31,25 @@
} }
// Material Design Item: Focused & Activated
// --------------------------------------------------
:host(.ion-focused.activated) .item-native {
background: var(--background-focused);
color: var(--color-focused);
}
:host(.ion-color.activated) .item-native {
background: current-color(base);
color: current-color(contrast);
}
:host(.ion-color.ion-focused.activated) .item-native {
background: current-color(shade);
color: current-color(contrast);
}
// Material Design Item Lines // Material Design Item Lines
// -------------------------------------------------- // --------------------------------------------------

View File

@ -7,6 +7,8 @@
/** /**
* @prop --background: Background of the item * @prop --background: Background of the item
* @prop --background-activated: Background of the item when pressed * @prop --background-activated: Background of the item when pressed
* @prop --background-focused: Background of the item when focused with the tab key
* @prop --background-hover: Background of the item on hover
* *
* @prop --border-color: Color of the item border * @prop --border-color: Color of the item border
* @prop --border-radius: Radius of the item border * @prop --border-radius: Radius of the item border
@ -16,6 +18,9 @@
* @prop --box-shadow: Box shadow of the item * @prop --box-shadow: Box shadow of the item
* *
* @prop --color: Color of the item * @prop --color: Color of the item
* @prop --color-activated: Color of the item when pressed
* @prop --color-focused: Color of the item when focused with the tab key
* @prop --color-hover: Color of the item on hover
* *
* @prop --detail-icon-color: Color of the item detail icon * @prop --detail-icon-color: Color of the item detail icon
* @prop --detail-icon-opacity: Opacity of the item detail icon * @prop --detail-icon-opacity: Opacity of the item detail icon
@ -63,6 +68,9 @@
--detail-icon-color: initial; --detail-icon-color: initial;
--detail-icon-font-size: 20px; --detail-icon-font-size: 20px;
--detail-icon-opacity: 0.25; --detail-icon-opacity: 0.25;
--color-activated: var(--color);
--color-focused: var(--color);
--color-hover: var(--color);
--ripple-color: var(--ion-item-background-activated, currentColor); --ripple-color: var(--ion-item-background-activated, currentColor);
@include font-smoothing(); @include font-smoothing();
@ -85,7 +93,7 @@
} }
// Item with Color // Item: Color
// -------------------------------------------------- // --------------------------------------------------
:host(.ion-color) .item-native { :host(.ion-color) .item-native {
@ -99,25 +107,53 @@
} }
// Activated Item // Item: Focused
// -------------------------------------------------- // --------------------------------------------------
:host(.ion-focused) .item-native { :host(.ion-focused) .item-native {
background: var(--background-focused); background: var(--background-focused);
color: var(--color-focused);
} }
:host(.ion-color.ion-focused) .item-native {
background: current-color(shade);
color: current-color(contrast);
}
// Item: Hover
// --------------------------------------------------
@media (any-hover: hover) {
:host(.ion-activatable:hover) .item-native {
background: var(--background-hover);
color: var(--color-hover);
}
:host(.ion-color.ion-activatable:hover) .item-native {
background: #{current-color(tint)};
color: #{current-color(contrast)};
}
}
// Item: Activated
// --------------------------------------------------
:host(.activated) .item-native { :host(.activated) .item-native {
background: var(--background-activated); background: var(--background-activated);
} color: var(--color-activated);
:host(.ion-color.activated) .item-native {
background: current-color(tint);
} }
// Disabled Item // Item: Disabled
// -------------------------------------------------- // --------------------------------------------------
:host(.item-interactive-disabled) {
cursor: default;
pointer-events: none;
}
:host(.item-disabled) { :host(.item-disabled) {
cursor: default; cursor: default;
opacity: .3; opacity: .3;
@ -209,7 +245,7 @@ button, a {
} }
// Item Detail Icon // Item Detail Icon
// ----------------------------------------- // --------------------------------------------------
.item-detail-icon { .item-detail-icon {
color: var(--detail-icon-color); color: var(--detail-icon-color);
@ -221,7 +257,7 @@ button, a {
// Item Slots // Item Slots
// ----------------------------------------- // --------------------------------------------------
::slotted(ion-icon) { ::slotted(ion-icon) {
font-size: 1.6em; font-size: 1.6em;
@ -242,7 +278,7 @@ button, a {
// Item Input // Item Input
// ----------------------------------------- // --------------------------------------------------
:host([vertical-align-top]), :host([vertical-align-top]),
:host(.item-input) { :host(.item-input) {
@ -331,7 +367,7 @@ button, a {
// Item Select // Item Select
// ----------------------------------------- // --------------------------------------------------
:host(.item-label-stacked) ::slotted(ion-select), :host(.item-label-stacked) ::slotted(ion-select),
:host(.item-label-floating) ::slotted(ion-select) { :host(.item-label-floating) ::slotted(ion-select) {
@ -346,7 +382,7 @@ button, a {
// Item Datetime // Item Datetime
// ----------------------------------------- // --------------------------------------------------
:host(.item-label-stacked) ::slotted(ion-datetime), :host(.item-label-stacked) ::slotted(ion-datetime),
:host(.item-label-floating) ::slotted(ion-datetime) { :host(.item-label-floating) ::slotted(ion-datetime) {
@ -357,9 +393,9 @@ button, a {
// Item w/ Multiple Inputs // Item w/ Multiple Inputs
// ----------------------------------------- // --------------------------------------------------
// Multiple inputs in an item should have the input cover // Multiple inputs in an item should have the input
// relative to them instead of the item // cover relative to themselves instead of the item
:host(.item-multiple-inputs) ::slotted(ion-datetime), :host(.item-multiple-inputs) ::slotted(ion-datetime),
:host(.item-multiple-inputs) ::slotted(ion-select) { :host(.item-multiple-inputs) ::slotted(ion-select) {
@ -368,7 +404,7 @@ button, a {
// Item Textarea // Item Textarea
// ----------------------------------------- // --------------------------------------------------
:host(.item-textarea) { :host(.item-textarea) {
align-items: stretch; align-items: stretch;

View File

@ -133,15 +133,31 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
this.multipleInputs = inputs.length > 1 ? true : false; this.multipleInputs = inputs.length > 1 ? true : false;
} }
// If the item contains an input including a radio, checkbox, datetime, etc.
// then the item will have a clickable input cover that should
// get the hover, focused and activated states UNLESS it has multiple
// inputs, then those need to individually get the click
private hasCover(): boolean {
const inputs = this.el.querySelectorAll('ion-checkbox, ion-datetime, ion-select, ion-radio');
return inputs.length > 0 && !this.multipleInputs;
}
// If the item has an href or button property it will render a native
// anchor or button that is clickable
private isClickable(): boolean { private isClickable(): boolean {
return (this.href !== undefined || this.button); return (this.href !== undefined || this.button);
} }
private canActivate(): boolean {
return (this.isClickable() || this.hasCover());
}
render() { render() {
const { detail, detailIcon, download, lines, disabled, href, rel, target, routerDirection } = this; const { detail, detailIcon, download, lines, disabled, href, rel, target, routerDirection } = this;
const childStyles = {}; const childStyles = {};
const mode = getIonMode(this); const mode = getIonMode(this);
const clickable = this.isClickable(); const clickable = this.isClickable();
const canActivate = this.canActivate();
const TagType = clickable ? (href === undefined ? 'button' : 'a') : 'div' as any; const TagType = clickable ? (href === undefined ? 'button' : 'a') : 'div' as any;
const attrs = (TagType === 'button') const attrs = (TagType === 'button')
? { type: this.type } ? { type: this.type }
@ -168,7 +184,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
'item-disabled': disabled, 'item-disabled': disabled,
'in-list': hostContext('ion-list', this.el), 'in-list': hostContext('ion-list', this.el),
'item-multiple-inputs': this.multipleInputs, 'item-multiple-inputs': this.multipleInputs,
'ion-activatable': this.isClickable(), 'ion-activatable': canActivate,
'ion-focusable': true, 'ion-focusable': true,
}} }}
> >
@ -187,7 +203,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon"></ion-icon>} {showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon"></ion-icon>}
<div class="item-inner-highlight"></div> <div class="item-inner-highlight"></div>
</div> </div>
{clickable && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>} {canActivate && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
</TagType> </TagType>
<div class="item-highlight"></div> <div class="item-highlight"></div>
</Host> </Host>

View File

@ -1396,12 +1396,17 @@ Item Inputs
| --------------------------- | ------------------------------------------------------------------------------------------------------------- | | --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `--background` | Background of the item | | `--background` | Background of the item |
| `--background-activated` | Background of the item when pressed | | `--background-activated` | Background of the item when pressed |
| `--background-focused` | Background of the item when focused with the tab key |
| `--background-hover` | Background of the item on hover |
| `--border-color` | Color of the item border | | `--border-color` | Color of the item border |
| `--border-radius` | Radius of the item border | | `--border-radius` | Radius of the item border |
| `--border-style` | Style of the item border | | `--border-style` | Style of the item border |
| `--border-width` | Width of the item border | | `--border-width` | Width of the item border |
| `--box-shadow` | Box shadow of the item | | `--box-shadow` | Box shadow of the item |
| `--color` | Color of the item | | `--color` | Color of the item |
| `--color-activated` | Color of the item when pressed |
| `--color-focused` | Color of the item when focused with the tab key |
| `--color-hover` | Color of the item on hover |
| `--detail-icon-color` | Color of the item detail icon | | `--detail-icon-color` | Color of the item detail icon |
| `--detail-icon-font-size` | Font size of the item detail icon | | `--detail-icon-font-size` | Font size of the item detail icon |
| `--detail-icon-opacity` | Opacity of the item detail icon | | `--detail-icon-opacity` | Opacity of the item detail icon |

View File

@ -41,21 +41,21 @@
<ion-label>button[ion-item] type="submit"</ion-label> <ion-label>button[ion-item] type="submit"</ion-label>
</ion-item> </ion-item>
<ion-item class="activated" onClick="testClick(event)"> <ion-item button class="activated" onClick="testClick(event)">
<ion-label>button[ion-item].activated</ion-label> <ion-label>button[ion-item].activated</ion-label>
</ion-item> </ion-item>
<ion-item color="danger" onclick="testClick(event)"> <ion-item button color="danger" onClick="testClick(event)">
<ion-label>button[ion-item] danger</ion-label> <ion-label>button[ion-item] danger</ion-label>
</ion-item> </ion-item>
<ion-item onclick="testClickOutsize(event)"> <ion-item button onclick="testClickOutsize(event)">
<ion-button slot="start" onclick="testClick(event)">Default</ion-button> <ion-button slot="start" onclick="testClick(event)">Default</ion-button>
<ion-label>Inner Buttons</ion-label> <ion-label>Inner Buttons</ion-label>
<ion-button fill="outline" slot="end" onclick="testClick(event)">Outline</ion-button> <ion-button fill="outline" slot="end" onclick="testClick(event)">Outline</ion-button>
</ion-item> </ion-item>
<ion-item disabled> <ion-item button disabled>
<ion-button slot="start" onclick="testClick(event)"> <ion-button slot="start" onclick="testClick(event)">
<ion-icon slot="start" name="home"></ion-icon> <ion-icon slot="start" name="home"></ion-icon>
Left Icon Left Icon

View File

@ -30,5 +30,7 @@ $toolbar-ios-color-activated: var(--ion-toolbar-color-activated, i
// -------------------------------------------------- // --------------------------------------------------
$item-ios-background: var(--ion-item-background, $background-color) !default; $item-ios-background: var(--ion-item-background, $background-color) !default;
$item-ios-background-activated: var(--ion-item-background-activated, var(--ion-color-step-150, #d9d9d9)) !default; $item-ios-background-activated: var(--ion-item-background-activated, var(--ion-color-step-150, #d9d9d9)) !default;
$item-ios-background-focused: var(--ion-item-background-focused, var(--ion-color-step-100, #e1e1e1)) !default;
$item-ios-background-hover: var(--ion-item-background-hover, var(--ion-color-step-50, #f5f5f5)) !default;
$item-ios-border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, #c8c7cc))) !default; $item-ios-border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, #c8c7cc))) !default;
$item-ios-color: var(--ion-item-color, $text-color) !default; $item-ios-color: var(--ion-item-color, $text-color) !default;

View File

@ -30,6 +30,8 @@ $toolbar-md-color-activated: var(--ion-toolbar-color-activated, #
// Material Design List & List Items // Material Design List & List Items
// -------------------------------------------------- // --------------------------------------------------
$item-md-background: var(--ion-item-background, $background-color) !default; $item-md-background: var(--ion-item-background, $background-color) !default;
$item-md-background-activated: var(--ion-item-background-activated, var(--ion-color-step-50, #f1f1f1)) !default; // activated item background does not exist in MD as it uses the ripple color
$item-md-background-focused: var(--ion-item-background-focused, var(--ion-color-step-100, #e1e1e1)) !default;
$item-md-background-hover: var(--ion-item-background-hover, var(--ion-color-step-50, #f5f5f5)) !default;
$item-md-border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, .13)))) !default; $item-md-border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, .13)))) !default;
$item-md-color: var(--ion-item-color, $text-color) !default; $item-md-color: var(--ion-item-color, $text-color) !default;