Compare commits

..

1 Commits

Author SHA1 Message Date
Brandy Smith
408065aa84 fix(list): migrate to scoped 2025-07-02 14:11:12 -04:00
8 changed files with 78 additions and 97 deletions

View File

@@ -4,9 +4,7 @@
// iOS List
// --------------------------------------------------
.list-ios {
@include margin($list-ios-margin-top, $list-ios-margin-end, $list-ios-margin-bottom, $list-ios-margin-start);
:host {
background: $item-ios-background;
}
@@ -14,7 +12,7 @@
// iOS Inset List
// --------------------------------------------------
.list-ios.list-inset {
:host(.list-inset) {
@include margin($list-inset-ios-margin-top, $list-inset-ios-margin-end, $list-inset-ios-margin-bottom, $list-inset-ios-margin-start);
@include border-radius($list-inset-ios-border-radius);
}
@@ -31,14 +29,14 @@
* items in an item-sliding when the item is the last
* element in the item-sliding container.
*/
.list-ios.list-inset ion-item:only-child,
.list-ios.list-inset ion-item:not(:only-of-type):last-of-type,
.list-ios.list-inset ion-item-sliding:last-of-type ion-item {
:host(.list-inset) ion-item:only-child,
:host(.list-inset) ion-item:not(:only-of-type):last-of-type,
:host(.list-inset) ion-item-sliding:last-of-type ion-item {
--border-width: 0;
--inner-border-width: 0;
}
.list-ios.list-inset + ion-list.list-inset {
:host(.list-inset) + ion-list.list-inset {
@include margin(0, null, null, null);
}
@@ -46,7 +44,7 @@
// iOS No Lines List
// --------------------------------------------------
.list-ios-lines-none .item-lines-default {
:host(.lines-none) .item-lines-default {
--inner-border-width: 0px;
--border-width: 0px;
}
@@ -54,7 +52,7 @@
// iOS Full Lines List
// --------------------------------------------------
.list-ios-lines-full .item-lines-default {
:host(.lines-full) .item-lines-default {
--inner-border-width: 0px;
--border-width: #{0 0 $list-ios-item-border-bottom-width 0};
}
@@ -62,7 +60,7 @@
// iOS Inset Lines List
// --------------------------------------------------
.list-ios-lines-inset .item-lines-default {
:host(.lines-inset) .item-lines-default {
--inner-border-width: #{0 0 $list-ios-item-border-bottom-width 0};
--border-width: 0px;
}
@@ -70,6 +68,6 @@
// iOS List Inside A Card
// --------------------------------------------------
ion-card .list-ios {
:host-context(ion-card) {
@include margin(0);
}

View File

@@ -4,32 +4,20 @@
// iOS List
// --------------------------------------------------
/// @prop - Margin top of the list
$list-ios-margin-top: null;
/// @prop - Margin end of the list
$list-ios-margin-end: null;
/// @prop - Margin bottom of the list
$list-ios-margin-bottom: null;
/// @prop - Margin start of the list
$list-ios-margin-start: null;
/// @prop - Border bottom of the item in a list
$list-ios-item-border-bottom-width: $item-ios-border-bottom-width;
$list-ios-item-border-bottom-width: $item-ios-border-bottom-width;
/// @prop - Margin top of the inset list
$list-inset-ios-margin-top: 16px;
$list-inset-ios-margin-top: 16px;
/// @prop - Margin end of the inset list
$list-inset-ios-margin-end: 16px;
$list-inset-ios-margin-end: $list-inset-ios-margin-top;
/// @prop - Margin bottom of the inset list
$list-inset-ios-margin-bottom: 16px;
$list-inset-ios-margin-bottom: $list-inset-ios-margin-top;
/// @prop - Margin start of the inset list
$list-inset-ios-margin-start: 16px;
$list-inset-ios-margin-start: $list-inset-ios-margin-top;
/// @prop - Border radius of the inset list
$list-inset-ios-border-radius: 10px;
$list-inset-ios-border-radius: 10px;

View File

@@ -4,21 +4,21 @@
// Material Design List
// --------------------------------------------------
.list-md {
@include margin($list-md-margin-top, $list-md-margin-end, $list-md-margin-bottom, $list-md-margin-start);
:host {
@include margin(0);
@include padding($list-md-padding-top, $list-md-padding-end, $list-md-padding-bottom, $list-md-padding-start);
background: $item-md-background;
}
.list-md > .input:last-child::after {
:host > .input:last-child::after {
@include position-horizontal(0, null);
}
// Material Design Inset List
// --------------------------------------------------
.list-md.list-inset {
:host(.list-inset) {
@include margin($list-inset-md-margin-top, $list-inset-md-margin-end, $list-inset-md-margin-bottom, $list-inset-md-margin-start);
@include border-radius($list-inset-md-border-radius);
}
@@ -34,8 +34,8 @@
* items in an item-sliding when the item is the last
* element in the item-sliding container.
*/
.list-md.list-inset ion-item:not(:only-of-type):last-of-type,
.list-md.list-inset ion-item-sliding:last-of-type ion-item {
:host(.list-inset) ion-item:not(:only-of-type):last-of-type,
:host(.list-inset) ion-item-sliding:last-of-type ion-item {
--border-width: 0;
--inner-border-width: 0;
}
@@ -46,12 +46,12 @@
* ion-item-sliding because the item will be the only
* one of its type inside of the ion-item-sliding group.
*/
.list-md.list-inset ion-item:only-child {
:host(.list-inset) ion-item:only-child {
--border-width: 0;
--inner-border-width: 0;
}
.list-md.list-inset + ion-list.list-inset {
:host(.list-inset) + ion-list.list-inset {
@include margin(0, null, null, null);
}
@@ -59,7 +59,7 @@
// Material Design No Lines List
// --------------------------------------------------
.list-md-lines-none .item-lines-default {
:host(.lines-none) .item-lines-default {
--inner-border-width: 0px;
--border-width: 0px;
}
@@ -67,7 +67,7 @@
// Material Design Full Lines List
// --------------------------------------------------
.list-md-lines-full .item-lines-default {
:host(.lines-full) .item-lines-default {
--inner-border-width: 0px;
--border-width: #{0 0 $list-md-item-border-bottom-width 0};
}
@@ -75,7 +75,7 @@
// Material Design Inset Lines List
// --------------------------------------------------
.list-md-lines-inset .item-lines-default {
:host(.lines-inset) .item-lines-default {
--inner-border-width: #{0 0 $list-md-item-border-bottom-width 0};
--border-width: 0px;
}
@@ -83,6 +83,6 @@
// Material Design List Inside A Card
// --------------------------------------------------
ion-card .list-md {
:host-context(ion-card) {
@include margin(0);
}

View File

@@ -4,44 +4,32 @@
// Material Design List
// --------------------------------------------------
/// @prop - Margin top of the list
$list-md-margin-top: 0;
/// @prop - Margin end of the list
$list-md-margin-end: 0;
/// @prop - Margin bottom of the list
$list-md-margin-bottom: 0;
/// @prop - Margin start of the list
$list-md-margin-start: 0;
/// @prop - Padding top of the list
$list-md-padding-top: 8px;
$list-md-padding-top: 8px;
/// @prop - Padding end of the list
$list-md-padding-end: 0;
$list-md-padding-end: 0;
/// @prop - Padding bottom of the list
$list-md-padding-bottom: 8px;
$list-md-padding-bottom: $list-md-padding-top;
/// @prop - Padding start of the list
$list-md-padding-start: 0;
$list-md-padding-start: $list-md-padding-end;
/// @prop - Border bottom of the item in a list
$list-md-item-border-bottom-width: $item-md-border-bottom-width;
$list-md-item-border-bottom-width: $item-md-border-bottom-width;
/// @prop - Margin top of the inset list
$list-inset-md-margin-top: 16px;
$list-inset-md-margin-top: 16px;
/// @prop - Margin end of the inset list
$list-inset-md-margin-end: 16px;
$list-inset-md-margin-end: $list-inset-md-margin-top;
/// @prop - Margin bottom of the inset list
$list-inset-md-margin-bottom: 16px;
$list-inset-md-margin-bottom: $list-inset-md-margin-top;
/// @prop - Margin start of the inset list
$list-inset-md-margin-start: 16px;
$list-inset-md-margin-start: $list-inset-md-margin-top;
/// @prop - Border radius of the inset list
$list-inset-md-border-radius: 2px;
$list-inset-md-border-radius: 2px;

View File

@@ -4,7 +4,7 @@
// List
// --------------------------------------------------
ion-list {
:host {
@include margin(0);
@include padding(0);
@@ -14,7 +14,7 @@ ion-list {
list-style-type: none;
}
ion-list.list-inset {
:host(.list-inset) {
transform: translateZ(0);
overflow: hidden;

View File

@@ -12,6 +12,7 @@ import { getIonMode } from '../../global/ionic-global';
ios: 'list.ios.scss',
md: 'list.md.scss',
},
scoped: true,
})
export class List implements ComponentInterface {
@Element() el!: HTMLElement;

View File

@@ -12,6 +12,14 @@
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
h2 {
margin-top: 20px;
margin-inline-start: 16px;
margin-bottom: 0;
}
</style>
</head>
<body>
@@ -23,6 +31,8 @@
</ion-header>
<ion-content id="content">
<h2>Default</h2>
<ion-list>
<ion-item>Pokémon Yellow</ion-item>
<ion-item>Super Metroid</ion-item>
@@ -30,6 +40,15 @@
<ion-item>The Legend of Zelda</ion-item>
<ion-item lines="full">Halo</ion-item>
</ion-list>
<h2>No padding</h2>
<ion-list class="ion-no-padding">
<ion-item>Pokémon Yellow</ion-item>
<ion-item>Super Metroid</ion-item>
<ion-item>Mega Man X</ion-item>
<ion-item>The Legend of Zelda</ion-item>
<ion-item lines="full">Halo</ion-item>
</ion-list>
</ion-content>
</ion-app>
</body>

View File

@@ -22,54 +22,39 @@ export interface FocusVisibleUtility {
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
let currentFocus: Element[] = [];
// Tracks if the last interaction was a pointer event (mouse, touch, pen)
// Used to distinguish between pointer and keyboard navigation for focus styling
let hadPointerEvent = false;
let keyboardMode = true;
const ref = rootEl ? rootEl.shadowRoot! : document;
const root = rootEl ? rootEl : document.body;
// Adds or removes the focused class for styling
const setFocus = (elements: Element[]) => {
currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
elements.forEach((el) => el.classList.add(ION_FOCUSED));
currentFocus = elements;
};
// Do not set focus on pointer interactions
const pointerDown = (ev: Event) => {
if (ev instanceof PointerEvent && ev.pointerType !== '') {
hadPointerEvent = true;
// Reset after the event loop so only the immediate focusin is suppressed
setTimeout(() => {
hadPointerEvent = false;
}, 0);
}
const pointerDown = () => {
keyboardMode = false;
setFocus([]);
};
// Clear hadPointerEvent so keyboard navigation shows focus
// Also, clear focus if the key is not a navigation key
const onKeydown = (ev: Event) => {
hadPointerEvent = false;
const keyboardEvent = ev as KeyboardEvent;
if (!FOCUS_KEYS.includes(keyboardEvent.key)) {
keyboardMode = FOCUS_KEYS.includes((ev as KeyboardEvent).key);
if (!keyboardMode) {
setFocus([]);
}
};
// Set focus if the last interaction was NOT a pointer event
// This works around iOS/Safari bugs where keydown is not fired for Tab
const onFocusin = (ev: Event) => {
const target = ev.target as HTMLElement;
if (target.classList.contains(ION_FOCUSABLE) && !hadPointerEvent) {
const toFocus = ev
.composedPath()
.filter((el): el is HTMLElement => el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE));
if (keyboardMode && ev.composedPath !== undefined) {
const toFocus = ev.composedPath().filter((el: any) => {
// TODO(FW-2832): type
if (el.classList) {
return el.classList.contains(ION_FOCUSABLE);
}
return false;
}) as Element[];
setFocus(toFocus);
}
};
const onFocusout = () => {
if (ref.activeElement === root) {
setFocus([]);
@@ -79,13 +64,15 @@ export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility =>
ref.addEventListener('keydown', onKeydown);
ref.addEventListener('focusin', onFocusin);
ref.addEventListener('focusout', onFocusout);
ref.addEventListener('pointerdown', pointerDown, { passive: true });
ref.addEventListener('touchstart', pointerDown, { passive: true });
ref.addEventListener('mousedown', pointerDown);
const destroy = () => {
ref.removeEventListener('keydown', onKeydown);
ref.removeEventListener('focusin', onFocusin);
ref.removeEventListener('focusout', onFocusout);
ref.removeEventListener('pointerdown', pointerDown);
ref.removeEventListener('touchstart', pointerDown);
ref.removeEventListener('mousedown', pointerDown);
};
return {