Skip to content

Commit 2c07a15

Browse files
feat(select): update popover interface to match MD spec on desktop, allow multiple values in popover interface (#23474)
resolves #23657 resolves #15500 resolves #12310
1 parent be219a2 commit 2c07a15

28 files changed

+889
-92
lines changed

core/src/components.d.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ export namespace Components {
208208
"translucent": boolean;
209209
}
210210
interface IonApp {
211+
"setFocus": (elements: HTMLElement[]) => Promise<void>;
211212
}
212213
interface IonAvatar {
213214
}
@@ -2358,19 +2359,23 @@ export namespace Components {
23582359
}
23592360
interface IonSelectPopover {
23602361
/**
2361-
* Header text for the popover
2362+
* The header text of the popover
23622363
*/
23632364
"header"?: string;
23642365
/**
2365-
* Text for popover body
2366+
* The text content of the popover body
23662367
*/
23672368
"message"?: string;
23682369
/**
2369-
* Array of options for the popover
2370+
* If true, the select accepts multiple values
2371+
*/
2372+
"multiple"?: boolean;
2373+
/**
2374+
* An array of options for the popover
23702375
*/
23712376
"options": SelectPopoverOption[];
23722377
/**
2373-
* Subheader text for the popover
2378+
* The subheader text of the popover
23742379
*/
23752380
"subHeader"?: string;
23762381
}
@@ -5946,19 +5951,23 @@ declare namespace LocalJSX {
59465951
}
59475952
interface IonSelectPopover {
59485953
/**
5949-
* Header text for the popover
5954+
* The header text of the popover
59505955
*/
59515956
"header"?: string;
59525957
/**
5953-
* Text for popover body
5958+
* The text content of the popover body
59545959
*/
59555960
"message"?: string;
59565961
/**
5957-
* Array of options for the popover
5962+
* If true, the select accepts multiple values
5963+
*/
5964+
"multiple"?: boolean;
5965+
/**
5966+
* An array of options for the popover
59585967
*/
59595968
"options"?: SelectPopoverOption[];
59605969
/**
5961-
* Subheader text for the popover
5970+
* The subheader text of the popover
59625971
*/
59635972
"subHeader"?: string;
59645973
}

core/src/components/app/app.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Build, Component, ComponentInterface, Element, Host, h } from '@stencil/core';
1+
import { Build, Component, ComponentInterface, Element, Host, Method, h } from '@stencil/core';
22

33
import { config } from '../../global/config';
44
import { getIonMode } from '../../global/ionic-global';
@@ -9,6 +9,8 @@ import { isPlatform } from '../../utils/platform';
99
styleUrl: 'app.scss',
1010
})
1111
export class App implements ComponentInterface {
12+
private focusVisible?: any;
13+
1214
@Element() el!: HTMLElement;
1315

1416
componentDidLoad() {
@@ -33,11 +35,28 @@ export class App implements ComponentInterface {
3335
if (typeof (window as any) !== 'undefined') {
3436
import('../../utils/keyboard/keyboard').then(module => module.startKeyboardAssist(window));
3537
}
36-
import('../../utils/focus-visible').then(module => module.startFocusVisible());
38+
import('../../utils/focus-visible').then(module => this.focusVisible = module.startFocusVisible());
3739
});
3840
}
3941
}
4042

43+
/**
44+
* @internal
45+
* Used to set focus on an element that uses `ion-focusable`.
46+
* Do not use this if focusing the element as a result of a keyboard
47+
* event as the focus utility should handle this for us. This method
48+
* should be used when we want to programmatically focus an element as
49+
* a result of another user action. (Ex: We focus the first element
50+
* inside of a popover when the user presents it, but the popover is not always
51+
* presented as a result of keyboard action.)
52+
*/
53+
@Method()
54+
async setFocus(elements: HTMLElement[]) {
55+
if (this.focusVisible) {
56+
this.focusVisible.setFocus(elements);
57+
}
58+
}
59+
4160
render() {
4261
const mode = getIonMode(this);
4362
return (

core/src/components/checkbox/readme.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,19 @@ export default defineComponent({
303303
| `--transition` | Transition of the checkbox icon |
304304

305305

306+
## Dependencies
307+
308+
### Used by
309+
310+
- ion-select-popover
311+
312+
### Graph
313+
```mermaid
314+
graph TD;
315+
ion-select-popover --> ion-checkbox
316+
style ion-checkbox fill:#f9f,stroke:#333,stroke-width:4px
317+
```
318+
306319
----------------------------------------------
307320

308321
*Built with [StencilJS](https://stenciljs.com/)*

core/src/components/datetime/datetime.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ export class Datetime implements ComponentInterface {
760760
}
761761

762762
connectedCallback() {
763-
this.clearFocusVisible = startFocusVisible(this.el);
763+
this.clearFocusVisible = startFocusVisible(this.el).destroy;
764764
}
765765

766766
disconnectedCallback() {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
--highlight-color-valid: #{$item-ios-input-highlight-color-valid};
2525
--highlight-color-invalid: #{$item-ios-input-highlight-color-invalid};
2626
--bottom-padding-start: 0px;
27+
2728
font-size: $item-ios-font-size;
2829
}
2930

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

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
transition: none;
100100
}
101101

102+
:host(.item-fill-outline.ion-focused) .item-native,
102103
:host(.item-fill-outline.item-has-focus) .item-native {
103104
border-color: transparent;
104105
}
@@ -308,6 +309,8 @@
308309
--padding-start: 0;
309310
}
310311

312+
:host(.ion-focused:not(.ion-color)) ::slotted(.label-stacked),
313+
:host(.ion-focused:not(.ion-color)) ::slotted(.label-floating),
311314
:host(.item-has-focus:not(.ion-color)) ::slotted(.label-stacked),
312315
:host(.item-has-focus:not(.ion-color)) ::slotted(.label-floating) {
313316
color: $label-md-text-color-focused;
@@ -347,24 +350,31 @@
347350
--border-color: #{$item-md-input-fill-border-color};
348351
}
349352

350-
:host(.item-fill-solid) .item-native:hover {
351-
--background: var(--background-hover);
352-
--border-color: #{$item-md-input-fill-border-color-hover};
353-
}
354-
353+
:host(.item-fill-solid.ion-focused) .item-native,
355354
:host(.item-fill-solid.item-has-focus) .item-native {
356355
--background: var(--background-focused);
356+
357357
border-bottom-color: var(--highlight-color-focused);
358358
}
359359

360360
:host(.item-fill-solid.item-shape-round) {
361361
@include border-radius(16px, 16px, 0, 0);
362362
}
363363

364+
@media (any-hover: hover) {
365+
:host(.item-fill-solid:hover) .item-native {
366+
--background: var(--background-hover);
367+
--border-color: #{$item-md-input-fill-border-color-hover};
368+
}
369+
}
370+
364371
// Material Design Item: Fill Outline
365372
// --------------------------------------------------
366373

367374
:host(.item-fill-outline) {
375+
--ripple-color: transparent;
376+
--background-focused: transparent;
377+
--background-hover: transparent;
368378
--border-color: #{$item-md-input-fill-border-color};
369379
--border-width: #{$item-md-border-bottom-width};
370380

@@ -379,10 +389,6 @@
379389
@include border-radius(4px);
380390
}
381391

382-
:host(.item-fill-outline) .item-native:hover {
383-
--border-color: #{$item-md-input-fill-border-color-hover};
384-
}
385-
386392
:host(.item-fill-outline.item-shape-round) .item-native {
387393
--inner-padding-start: 16px;
388394

@@ -393,14 +399,22 @@
393399
@include padding-horizontal(32px, null);
394400
}
395401

396-
402+
:host(.item-fill-outline.item-label-floating.ion-focused) .item-native ::slotted(ion-input:not(:first-child)),
403+
:host(.item-fill-outline.item-label-floating.ion-focused) .item-native ::slotted(ion-textarea:not(:first-child)),
397404
:host(.item-fill-outline.item-label-floating.item-has-focus) .item-native ::slotted(ion-input:not(:first-child)),
405+
:host(.item-fill-outline.item-label-floating.item-has-focus) .item-native ::slotted(ion-textarea:not(:first-child)),
398406
:host(.item-fill-outline.item-label-floating.item-has-value) .item-native ::slotted(ion-input:not(:first-child)),
399-
:host(.item-fill-outline.item-label-floating.item-has-focus) .item-native ::slotted(ion-textarea:not(:first-child)),
400407
:host(.item-fill-outline.item-label-floating.item-has-value) .item-native ::slotted(ion-textarea:not(:first-child)) {
401408
transform: translateY(-25%);
402409
}
403410

411+
@media (any-hover: hover) {
412+
:host(.item-fill-outline:hover) .item-native {
413+
--border-color: #{$item-md-input-fill-border-color-hover};
414+
}
415+
}
416+
417+
404418
// Material Design Item: Invalid
405419
// --------------------------------------------------
406420

@@ -416,4 +430,4 @@
416430
:host(.item-fill-solid.ion-invalid:not(.ion-color)) .item-native,
417431
:host(.item-fill-solid.ion-invalid:not(.ion-color)) .item-highlight {
418432
border-color: var(--highlight-color-invalid);
419-
}
433+
}

core/src/components/item/item.scss

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
// --------------------------------------------------
155155

156156
@media (any-hover: hover) {
157-
:host(.ion-activatable:hover) .item-native {
157+
:host(.ion-activatable:not(.ion-focused):hover) .item-native {
158158
color: var(--color-hover);
159159

160160
&::after {
@@ -164,7 +164,7 @@
164164
}
165165
}
166166

167-
:host(.ion-color.ion-activatable:hover) .item-native {
167+
:host(.ion-color.ion-activatable:not(.ion-focused):hover) .item-native {
168168
color: #{current-color(contrast)};
169169

170170
&::after {
@@ -173,6 +173,7 @@
173173
}
174174
}
175175

176+
176177
// Item: Disabled
177178
// --------------------------------------------------
178179

@@ -308,7 +309,11 @@ button, a {
308309
z-index: 1;
309310
}
310311

312+
// Setting pointer-events to none allows the label
313+
// to be clicked to open the select interface
311314
::slotted(ion-label) {
315+
pointer-events: none;
316+
312317
flex: 1;
313318
}
314319

@@ -359,7 +364,7 @@ button, a {
359364
width: 100%;
360365

361366
height: 100%;
362-
367+
363368
transform: scaleX(0);
364369

365370
transition: transform 200ms, border-bottom-width 200ms;
@@ -370,6 +375,8 @@ button, a {
370375
pointer-events: none;
371376
}
372377

378+
:host(.ion-focused) .item-highlight,
379+
:host(.ion-focused) .item-inner-highlight,
373380
:host(.item-has-focus) .item-highlight,
374381
:host(.item-has-focus) .item-inner-highlight {
375382
transform: scaleX(1);
@@ -378,22 +385,27 @@ button, a {
378385
border-color: var(--highlight-background);
379386
}
380387

388+
:host(.ion-focused) .item-highlight,
381389
:host(.item-has-focus) .item-highlight {
382390
border-width: var(--full-highlight-height);
383391

384392
opacity: var(--show-full-highlight);
385393
}
386394

395+
:host(.ion-focused) .item-inner-highlight,
387396
:host(.item-has-focus) .item-inner-highlight {
388397
border-bottom-width: var(--inset-highlight-height);
389398

390399
opacity: var(--show-inset-highlight);
391400
}
392401

402+
:host(.ion-focused.item-fill-solid) .item-highlight,
393403
:host(.item-has-focus.item-fill-solid) .item-highlight {
394404
border-width: calc(var(--full-highlight-height) - 1px);
395405
}
396406

407+
:host(.ion-focused) .item-inner-highlight,
408+
:host(.ion-focused:not(.item-fill-outline)) .item-highlight,
397409
:host(.item-has-focus) .item-inner-highlight,
398410
:host(.item-has-focus:not(.item-fill-outline)) .item-highlight {
399411
border-top: none;
@@ -405,6 +417,7 @@ button, a {
405417
// Item Input Focused
406418
// --------------------------------------------------
407419

420+
:host(.item-interactive.ion-focused),
408421
:host(.item-interactive.item-has-focus),
409422
:host(.item-interactive.ion-touched.ion-invalid) {
410423
// If the item has a full border and highlight is enabled, show the full item highlight
@@ -417,6 +430,7 @@ button, a {
417430
// Item Input Focus
418431
// --------------------------------------------------
419432

433+
:host(.item-interactive.ion-focused),
420434
:host(.item-interactive.item-has-focus) {
421435
--highlight-background: var(--highlight-color-focused);
422436
}
@@ -553,4 +567,4 @@ ion-ripple-effect {
553567
display: none;
554568

555569
color: var(--highlight-color-invalid);
556-
}
570+
}

0 commit comments

Comments
 (0)