mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
408065aa84 |
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user