Skip to content

Commit 8a88dd2

Browse files
feat(item): add hover and focused states (#18606)
references #18279 references #17624
1 parent ad00679 commit 8a88dd2

File tree

10 files changed

+139
-27
lines changed

10 files changed

+139
-27
lines changed

core/api.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,12 +483,17 @@ ion-item,prop,target,string | undefined,undefined,false,false
483483
ion-item,prop,type,"button" | "reset" | "submit",'button',false,false
484484
ion-item,css-prop,--background
485485
ion-item,css-prop,--background-activated
486+
ion-item,css-prop,--background-focused
487+
ion-item,css-prop,--background-hover
486488
ion-item,css-prop,--border-color
487489
ion-item,css-prop,--border-radius
488490
ion-item,css-prop,--border-style
489491
ion-item,css-prop,--border-width
490492
ion-item,css-prop,--box-shadow
491493
ion-item,css-prop,--color
494+
ion-item,css-prop,--color-activated
495+
ion-item,css-prop,--color-focused
496+
ion-item,css-prop,--color-hover
492497
ion-item,css-prop,--detail-icon-color
493498
ion-item,css-prop,--detail-icon-font-size
494499
ion-item,css-prop,--detail-icon-opacity

core/src/components/checkbox/checkbox.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ export class Checkbox implements ComponentInterface {
9494
}
9595

9696
@Watch('disabled')
97-
emitStyle() {
97+
disabledChanged() {
98+
this.emitStyle();
99+
}
100+
101+
private emitStyle() {
98102
this.ionStyle.emit({
99103
'checkbox-checked': this.checked,
100104
'interactive-disabled': this.disabled,

core/src/components/item/item.ios.scss

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
--inner-border-width: #{0px 0px $item-ios-border-bottom-width 0px};
1313
--background: #{$item-ios-background};
1414
--background-activated: #{$item-ios-background-activated};
15-
--background-focused: #{$item-ios-background-activated};
15+
--background-focused: #{$item-ios-background-focused};
16+
--background-hover: #{$item-ios-background-hover};
1617
--border-color: #{$item-ios-border-bottom-color};
1718
--color: #{$item-ios-color};
1819
--highlight-height: 0;
@@ -23,10 +24,31 @@
2324
font-size: $item-ios-font-size;
2425
}
2526

27+
28+
// iOS Activated
29+
// --------------------------------------------------
30+
2631
:host(.activated) {
2732
--transition: none;
2833
}
2934

35+
:host(.ion-color.activated) .item-native {
36+
background: current-color(shade);
37+
color: current-color(contrast);
38+
}
39+
40+
@media (any-hover: hover) {
41+
:host(.activated.ion-activatable:hover) .item-native {
42+
background: var(--background-activated);
43+
color: var(--color-activated);
44+
}
45+
46+
:host(.activated.ion-color.ion-activatable:hover) .item-native {
47+
background: #{current-color(shade)};
48+
color: #{current-color(contrast)};
49+
}
50+
}
51+
3052

3153
// iOS Item Lines
3254
// --------------------------------------------------

core/src/components/item/item.md.scss

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
--min-height: #{$item-md-min-height};
1010
--background: #{$item-md-background};
1111
--background-activated: var(--background);
12-
--background-focused: #{$item-md-background-activated};
12+
--background-focused: #{$item-md-background-focused};
13+
--background-hover: #{$item-md-background-hover};
1314
--border-color: #{$item-md-border-bottom-color};
1415
--color: #{$item-md-color};
1516
--transition: background-color 300ms cubic-bezier(.4, 0, .2, 1);
@@ -30,6 +31,25 @@
3031
}
3132

3233

34+
// Material Design Item: Focused & Activated
35+
// --------------------------------------------------
36+
37+
:host(.ion-focused.activated) .item-native {
38+
background: var(--background-focused);
39+
color: var(--color-focused);
40+
}
41+
42+
:host(.ion-color.activated) .item-native {
43+
background: current-color(base);
44+
color: current-color(contrast);
45+
}
46+
47+
:host(.ion-color.ion-focused.activated) .item-native {
48+
background: current-color(shade);
49+
color: current-color(contrast);
50+
}
51+
52+
3353
// Material Design Item Lines
3454
// --------------------------------------------------
3555

core/src/components/item/item.scss

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
/**
88
* @prop --background: Background of the item
99
* @prop --background-activated: Background of the item when pressed
10+
* @prop --background-focused: Background of the item when focused with the tab key
11+
* @prop --background-hover: Background of the item on hover
1012
*
1113
* @prop --border-color: Color of the item border
1214
* @prop --border-radius: Radius of the item border
@@ -16,6 +18,9 @@
1618
* @prop --box-shadow: Box shadow of the item
1719
*
1820
* @prop --color: Color of the item
21+
* @prop --color-activated: Color of the item when pressed
22+
* @prop --color-focused: Color of the item when focused with the tab key
23+
* @prop --color-hover: Color of the item on hover
1924
*
2025
* @prop --detail-icon-color: Color of the item detail icon
2126
* @prop --detail-icon-opacity: Opacity of the item detail icon
@@ -63,6 +68,9 @@
6368
--detail-icon-color: initial;
6469
--detail-icon-font-size: 20px;
6570
--detail-icon-opacity: 0.25;
71+
--color-activated: var(--color);
72+
--color-focused: var(--color);
73+
--color-hover: var(--color);
6674
--ripple-color: var(--ion-item-background-activated, currentColor);
6775

6876
@include font-smoothing();
@@ -85,7 +93,7 @@
8593
}
8694

8795

88-
// Item with Color
96+
// Item: Color
8997
// --------------------------------------------------
9098

9199
:host(.ion-color) .item-native {
@@ -99,25 +107,53 @@
99107
}
100108

101109

102-
// Activated Item
110+
// Item: Focused
103111
// --------------------------------------------------
104112

105113
:host(.ion-focused) .item-native {
106114
background: var(--background-focused);
115+
color: var(--color-focused);
107116
}
108117

109-
:host(.activated) .item-native {
110-
background: var(--background-activated);
118+
:host(.ion-color.ion-focused) .item-native {
119+
background: current-color(shade);
120+
color: current-color(contrast);
111121
}
112122

113-
:host(.ion-color.activated) .item-native {
114-
background: current-color(tint);
123+
124+
// Item: Hover
125+
// --------------------------------------------------
126+
127+
@media (any-hover: hover) {
128+
:host(.ion-activatable:hover) .item-native {
129+
background: var(--background-hover);
130+
color: var(--color-hover);
131+
}
132+
133+
:host(.ion-color.ion-activatable:hover) .item-native {
134+
background: #{current-color(tint)};
135+
color: #{current-color(contrast)};
136+
}
115137
}
116138

117139

118-
// Disabled Item
140+
// Item: Activated
119141
// --------------------------------------------------
120142

143+
:host(.activated) .item-native {
144+
background: var(--background-activated);
145+
color: var(--color-activated);
146+
}
147+
148+
149+
// Item: Disabled
150+
// --------------------------------------------------
151+
152+
:host(.item-interactive-disabled) {
153+
cursor: default;
154+
pointer-events: none;
155+
}
156+
121157
:host(.item-disabled) {
122158
cursor: default;
123159
opacity: .3;
@@ -209,7 +245,7 @@ button, a {
209245
}
210246

211247
// Item Detail Icon
212-
// -----------------------------------------
248+
// --------------------------------------------------
213249

214250
.item-detail-icon {
215251
color: var(--detail-icon-color);
@@ -221,7 +257,7 @@ button, a {
221257

222258

223259
// Item Slots
224-
// -----------------------------------------
260+
// --------------------------------------------------
225261

226262
::slotted(ion-icon) {
227263
font-size: 1.6em;
@@ -242,7 +278,7 @@ button, a {
242278

243279

244280
// Item Input
245-
// -----------------------------------------
281+
// --------------------------------------------------
246282

247283
:host([vertical-align-top]),
248284
:host(.item-input) {
@@ -331,7 +367,7 @@ button, a {
331367

332368

333369
// Item Select
334-
// -----------------------------------------
370+
// --------------------------------------------------
335371

336372
:host(.item-label-stacked) ::slotted(ion-select),
337373
:host(.item-label-floating) ::slotted(ion-select) {
@@ -346,7 +382,7 @@ button, a {
346382

347383

348384
// Item Datetime
349-
// -----------------------------------------
385+
// --------------------------------------------------
350386

351387
:host(.item-label-stacked) ::slotted(ion-datetime),
352388
:host(.item-label-floating) ::slotted(ion-datetime) {
@@ -357,9 +393,9 @@ button, a {
357393

358394

359395
// Item w/ Multiple Inputs
360-
// -----------------------------------------
361-
// Multiple inputs in an item should have the input cover
362-
// relative to them instead of the item
396+
// --------------------------------------------------
397+
// Multiple inputs in an item should have the input
398+
// cover relative to themselves instead of the item
363399

364400
:host(.item-multiple-inputs) ::slotted(ion-datetime),
365401
:host(.item-multiple-inputs) ::slotted(ion-select) {
@@ -368,7 +404,7 @@ button, a {
368404

369405

370406
// Item Textarea
371-
// -----------------------------------------
407+
// --------------------------------------------------
372408

373409
:host(.item-textarea) {
374410
align-items: stretch;

core/src/components/item/item.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,31 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
133133
this.multipleInputs = inputs.length > 1 ? true : false;
134134
}
135135

136+
// If the item contains an input including a radio, checkbox, datetime, etc.
137+
// then the item will have a clickable input cover that should
138+
// get the hover, focused and activated states UNLESS it has multiple
139+
// inputs, then those need to individually get the click
140+
private hasCover(): boolean {
141+
const inputs = this.el.querySelectorAll('ion-checkbox, ion-datetime, ion-select, ion-radio');
142+
return inputs.length > 0 && !this.multipleInputs;
143+
}
144+
145+
// If the item has an href or button property it will render a native
146+
// anchor or button that is clickable
136147
private isClickable(): boolean {
137148
return (this.href !== undefined || this.button);
138149
}
139150

151+
private canActivate(): boolean {
152+
return (this.isClickable() || this.hasCover());
153+
}
154+
140155
render() {
141156
const { detail, detailIcon, download, lines, disabled, href, rel, target, routerDirection } = this;
142157
const childStyles = {};
143158
const mode = getIonMode(this);
144159
const clickable = this.isClickable();
160+
const canActivate = this.canActivate();
145161
const TagType = clickable ? (href === undefined ? 'button' : 'a') : 'div' as any;
146162
const attrs = (TagType === 'button')
147163
? { type: this.type }
@@ -168,7 +184,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
168184
'item-disabled': disabled,
169185
'in-list': hostContext('ion-list', this.el),
170186
'item-multiple-inputs': this.multipleInputs,
171-
'ion-activatable': this.isClickable(),
187+
'ion-activatable': canActivate,
172188
'ion-focusable': true,
173189
}}
174190
>
@@ -187,7 +203,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
187203
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon"></ion-icon>}
188204
<div class="item-inner-highlight"></div>
189205
</div>
190-
{clickable && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
206+
{canActivate && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
191207
</TagType>
192208
<div class="item-highlight"></div>
193209
</Host>

core/src/components/item/readme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,12 +1396,17 @@ Item Inputs
13961396
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
13971397
| `--background` | Background of the item |
13981398
| `--background-activated` | Background of the item when pressed |
1399+
| `--background-focused` | Background of the item when focused with the tab key |
1400+
| `--background-hover` | Background of the item on hover |
13991401
| `--border-color` | Color of the item border |
14001402
| `--border-radius` | Radius of the item border |
14011403
| `--border-style` | Style of the item border |
14021404
| `--border-width` | Width of the item border |
14031405
| `--box-shadow` | Box shadow of the item |
14041406
| `--color` | Color of the item |
1407+
| `--color-activated` | Color of the item when pressed |
1408+
| `--color-focused` | Color of the item when focused with the tab key |
1409+
| `--color-hover` | Color of the item on hover |
14051410
| `--detail-icon-color` | Color of the item detail icon |
14061411
| `--detail-icon-font-size` | Font size of the item detail icon |
14071412
| `--detail-icon-opacity` | Opacity of the item detail icon |

core/src/components/item/test/buttons/index.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@
4141
<ion-label>button[ion-item] type="submit"</ion-label>
4242
</ion-item>
4343

44-
<ion-item class="activated" onClick="testClick(event)">
44+
<ion-item button class="activated" onClick="testClick(event)">
4545
<ion-label>button[ion-item].activated</ion-label>
4646
</ion-item>
4747

48-
<ion-item color="danger" onclick="testClick(event)">
48+
<ion-item button color="danger" onClick="testClick(event)">
4949
<ion-label>button[ion-item] danger</ion-label>
5050
</ion-item>
5151

52-
<ion-item onclick="testClickOutsize(event)">
52+
<ion-item button onclick="testClickOutsize(event)">
5353
<ion-button slot="start" onclick="testClick(event)">Default</ion-button>
5454
<ion-label>Inner Buttons</ion-label>
5555
<ion-button fill="outline" slot="end" onclick="testClick(event)">Outline</ion-button>
5656
</ion-item>
5757

58-
<ion-item disabled>
58+
<ion-item button disabled>
5959
<ion-button slot="start" onclick="testClick(event)">
6060
<ion-icon slot="start" name="home"></ion-icon>
6161
Left Icon

core/src/themes/ionic.theme.default.ios.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ $toolbar-ios-color-activated: var(--ion-toolbar-color-activated, i
3030
// --------------------------------------------------
3131
$item-ios-background: var(--ion-item-background, $background-color) !default;
3232
$item-ios-background-activated: var(--ion-item-background-activated, var(--ion-color-step-150, #d9d9d9)) !default;
33+
$item-ios-background-focused: var(--ion-item-background-focused, var(--ion-color-step-100, #e1e1e1)) !default;
34+
$item-ios-background-hover: var(--ion-item-background-hover, var(--ion-color-step-50, #f5f5f5)) !default;
3335
$item-ios-border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, #c8c7cc))) !default;
34-
$item-ios-color: var(--ion-item-color, $text-color) !default;
36+
$item-ios-color: var(--ion-item-color, $text-color) !default;

core/src/themes/ionic.theme.default.md.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ $toolbar-md-color-activated: var(--ion-toolbar-color-activated, #
3030
// Material Design List & List Items
3131
// --------------------------------------------------
3232
$item-md-background: var(--ion-item-background, $background-color) !default;
33-
$item-md-background-activated: var(--ion-item-background-activated, var(--ion-color-step-50, #f1f1f1)) !default;
33+
// activated item background does not exist in MD as it uses the ripple color
34+
$item-md-background-focused: var(--ion-item-background-focused, var(--ion-color-step-100, #e1e1e1)) !default;
35+
$item-md-background-hover: var(--ion-item-background-hover, var(--ion-color-step-50, #f5f5f5)) !default;
3436
$item-md-border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, .13)))) !default;
3537
$item-md-color: var(--ion-item-color, $text-color) !default;

0 commit comments

Comments
 (0)