mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(item-sliding): use the correct gesture direction and side for rtl (#18366)
references #17012
This commit is contained in:
@@ -477,7 +477,7 @@ ion-item-sliding,method,close,close() => Promise<void>
|
||||
ion-item-sliding,method,closeOpened,closeOpened() => Promise<boolean>
|
||||
ion-item-sliding,method,getOpenAmount,getOpenAmount() => Promise<number>
|
||||
ion-item-sliding,method,getSlidingRatio,getSlidingRatio() => Promise<number>
|
||||
ion-item-sliding,method,open,open(side: string | undefined) => Promise<void>
|
||||
ion-item-sliding,method,open,open(side: "start" | "end" | undefined) => Promise<void>
|
||||
ion-item-sliding,event,ionDrag,void,true
|
||||
|
||||
ion-item,shadow
|
||||
|
||||
2
core/src/components.d.ts
vendored
2
core/src/components.d.ts
vendored
@@ -2026,7 +2026,7 @@ export namespace Components {
|
||||
/**
|
||||
* Open the sliding item.
|
||||
*/
|
||||
'open': (side: string | undefined) => Promise<void>;
|
||||
'open': (side: "start" | "end" | undefined) => Promise<void>;
|
||||
}
|
||||
interface IonItemSlidingAttributes extends StencilHTMLAttributes {
|
||||
/**
|
||||
|
||||
@@ -38,6 +38,7 @@ export class ItemOptions implements ComponentInterface {
|
||||
|
||||
hostData() {
|
||||
const isEnd = isEndSide(this.win, this.side);
|
||||
|
||||
return {
|
||||
class: {
|
||||
[`${this.mode}`]: true,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, QueueApi, State, Watch } from '@stencil/core';
|
||||
|
||||
import { Gesture, GestureDetail, Mode } from '../../interface';
|
||||
import { Gesture, GestureDetail, Mode, Side } from '../../interface';
|
||||
import { isEndSide } from '../../utils/helpers';
|
||||
|
||||
const SWIPE_MARGIN = 30;
|
||||
const ELASTIC_FACTOR = 0.55;
|
||||
@@ -122,9 +123,8 @@ export class ItemSliding implements ComponentInterface {
|
||||
*
|
||||
* @param side The side of the options to open. If a side is not provided, it will open the first set of options it finds within the item.
|
||||
*/
|
||||
// TODO update to work with RTL
|
||||
@Method()
|
||||
async open(side: string | undefined) {
|
||||
async open(side: Side | undefined) {
|
||||
if (this.item === null) { return; }
|
||||
|
||||
const optionsToOpen = this.getOptions(side);
|
||||
@@ -138,6 +138,9 @@ export class ItemSliding implements ComponentInterface {
|
||||
side = (optionsToOpen === this.leftOptions) ? 'start' : 'end';
|
||||
}
|
||||
|
||||
// In RTL we want to switch the sides
|
||||
side = isEndSide(window, side) ? 'end' : 'start';
|
||||
|
||||
const isStartOpen = this.openAmount < 0;
|
||||
const isEndOpen = this.openAmount > 0;
|
||||
|
||||
@@ -185,20 +188,20 @@ export class ItemSliding implements ComponentInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a side, attempt to return the ion-item-options element
|
||||
* Given an optional side, return the ion-item-options element.
|
||||
*
|
||||
* @param side This side of the options to get. If a side is not provided it will return the first one available
|
||||
* @param side This side of the options to get. If a side is not provided it will
|
||||
* return the first one available.
|
||||
*/
|
||||
// TODO update to work with RTL
|
||||
private getOptions(side?: string): HTMLIonItemOptionsElement | undefined {
|
||||
if (side === undefined) {
|
||||
return this.leftOptions || this.rightOptions;
|
||||
} else if (side === 'start') {
|
||||
return this.leftOptions;
|
||||
} else {
|
||||
return this.rightOptions;
|
||||
}
|
||||
if (side === undefined) {
|
||||
return this.leftOptions || this.rightOptions;
|
||||
} else if (side === 'start') {
|
||||
return this.leftOptions;
|
||||
} else {
|
||||
return this.rightOptions;
|
||||
}
|
||||
}
|
||||
|
||||
private async updateOptions() {
|
||||
const options = this.el.querySelectorAll('ion-item-options');
|
||||
@@ -211,7 +214,9 @@ export class ItemSliding implements ComponentInterface {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const option = await options.item(i).componentOnReady();
|
||||
|
||||
if (option.side === 'start') {
|
||||
const side = isEndSide(window, option.side) ? 'end' : 'start';
|
||||
|
||||
if (side === 'start') {
|
||||
this.leftOptions = option;
|
||||
sides |= ItemSide.Start;
|
||||
} else {
|
||||
|
||||
@@ -692,15 +692,15 @@ Type: `Promise<number>`
|
||||
|
||||
|
||||
|
||||
### `open(side: string | undefined) => Promise<void>`
|
||||
### `open(side: "start" | "end" | undefined) => Promise<void>`
|
||||
|
||||
Open the sliding item.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `side` | `string \| undefined` | The side of the options to open. If a side is not provided, it will open the first set of options it finds within the item. |
|
||||
| Name | Type | Description |
|
||||
| ------ | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `side` | `"end" \| "start" \| undefined` | The side of the options to open. If a side is not provided, it will open the first set of options it finds within the item. |
|
||||
|
||||
#### Returns
|
||||
|
||||
|
||||
@@ -62,11 +62,11 @@
|
||||
</ion-item>
|
||||
<ion-item-options class="show-options">
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon name="more"></ion-icon>
|
||||
<ion-icon slot="start" name="more"></ion-icon>
|
||||
<span class="more-text"></span>
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('item6')">
|
||||
<ion-icon name="archive"></ion-icon>
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
<span class="archive-text"></span>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
@@ -85,11 +85,11 @@
|
||||
</ion-item-options>
|
||||
<ion-item-options side="end" class="show-options">
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon name="more"></ion-icon>
|
||||
<ion-icon slot="start" name="more"></ion-icon>
|
||||
<span class="more-text"></span>
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('item6')">
|
||||
<ion-icon name="archive"></ion-icon>
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
<span class="archive-text"></span>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
@@ -183,16 +183,19 @@
|
||||
</ion-item>
|
||||
<ion-item-options side="start" class="sliding-enabled">
|
||||
<ion-item-option color="secondary" expandable onclick="unread('item2')">
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Unread
|
||||
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
|
||||
Unread
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="end" class="sliding-enabled">
|
||||
<ion-item-option color="primary" onclick="archive('item2')">
|
||||
<ion-icon name="mail"></ion-icon>Archive
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" onclick="del('item2')" expandable>
|
||||
<ion-icon name="trash"></ion-icon>Delete
|
||||
<ion-icon slot="start" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
@@ -206,16 +209,19 @@
|
||||
</ion-item>
|
||||
<ion-item-options side="start" icon-start class="sliding-enabled">
|
||||
<ion-item-option color="secondary" expandable onclick="unread('item3')">
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Unread
|
||||
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
|
||||
Unread
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary" onclick="archive('item3')">
|
||||
<ion-icon name="mail"></ion-icon>Archive
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" onclick="del('item3')" expandable class="sliding-enabled">
|
||||
<ion-icon name="trash"></ion-icon>Delete
|
||||
<ion-icon slot="start" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
@@ -230,7 +236,8 @@
|
||||
</ion-item>
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary" onclick="archive('item4')" expandable class="sliding-enabled">
|
||||
<ion-icon name="archive"></ion-icon>Archive
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
@@ -247,13 +254,16 @@
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary" expandable>
|
||||
<ion-icon name="more"></ion-icon>More
|
||||
<ion-icon slot="start" name="more"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('item5')">
|
||||
<ion-icon name="archive"></ion-icon>Archive
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="light" onclick="del('item5')">
|
||||
<ion-icon name="trash"></ion-icon>Delete
|
||||
<ion-icon slot="start" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
@@ -267,11 +277,11 @@
|
||||
</ion-item>
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon name="more"></ion-icon>
|
||||
<ion-icon slot="start" name="more"></ion-icon>
|
||||
<span class="more-text"></span>
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('item7')">
|
||||
<ion-icon name="archive"></ion-icon>
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
<span class="archive-text"></span>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
@@ -292,10 +302,10 @@
|
||||
<ion-icon name="archive"></ion-icon>Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" expandable onclick="download('item8')">
|
||||
<ion-icon name="download" class="download-hide"></ion-icon>
|
||||
<ion-icon slot="start" name="download" class="download-hide"></ion-icon>
|
||||
<div class="download-hide">Download</div>
|
||||
|
||||
<ion-icon class="download-spinner" name="refresh"></ion-icon>
|
||||
<ion-icon slot="start" class="download-spinner" name="refresh"></ion-icon>
|
||||
<div class="download-spinner">Loading...</div>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
@@ -322,31 +332,39 @@
|
||||
</ion-item>
|
||||
<ion-item-options side="start" class="sliding-enabled">
|
||||
<ion-item-option color="primary" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 1
|
||||
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
|
||||
Btn 1
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 2
|
||||
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
|
||||
Btn 2
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 3
|
||||
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
|
||||
Btn 3
|
||||
</ion-item-option>
|
||||
<ion-item-option color="tertiary" expandable>
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Btn 4
|
||||
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
|
||||
Btn 4
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="end" class="sliding-enabled">
|
||||
<ion-item-option color="primary" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 5
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Btn 5
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 6
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Btn 6
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 7
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Btn 7
|
||||
</ion-item-option>
|
||||
<ion-item-option color="tertiary" expandable>
|
||||
<ion-icon name="mail"></ion-icon>Btn 8
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Btn 8
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
@@ -412,7 +430,7 @@
|
||||
var item = document.getElementById('item2');
|
||||
item.open(side);
|
||||
}
|
||||
|
||||
|
||||
function openItemOneSide() {
|
||||
var item = document.getElementById('item1');
|
||||
item.open();
|
||||
|
||||
@@ -29,3 +29,31 @@ test('item-sliding: standalone', async () => {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
test('item-sliding:rtl: standalone', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item-sliding/test/standalone?ionic:_testing=true&rtl=true'
|
||||
});
|
||||
|
||||
const compares = [];
|
||||
compares.push(await page.compareScreenshot());
|
||||
|
||||
// Pass sliding item with start icons in option
|
||||
await openItemSliding('#startItem', page, true);
|
||||
compares.push(await page.compareScreenshot(`start icons open`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
// Pass sliding item with top icons in option
|
||||
await openItemSliding('#topItem', page, true);
|
||||
compares.push(await page.compareScreenshot(`top icons open`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
// Pass sliding item with anchor option
|
||||
await openItemSliding('#anchorItem', page, true);
|
||||
compares.push(await page.compareScreenshot(`anchor option`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Opens a sliding item by simulating a drag event
|
||||
export async function openItemSliding(id: string, page: any) {
|
||||
export async function openItemSliding(id: string, page: any, rtl = false) {
|
||||
try {
|
||||
const slidingItem = await page.$(id);
|
||||
|
||||
@@ -8,10 +8,15 @@ export async function openItemSliding(id: string, page: any) {
|
||||
const centerX = parseFloat(boundingBox.x + boundingBox.width / 2);
|
||||
const centerY = parseFloat(boundingBox.y + boundingBox.height / 2);
|
||||
|
||||
let endX = 0;
|
||||
if (rtl) {
|
||||
endX = boundingBox.width;
|
||||
}
|
||||
|
||||
await page.mouse.move(centerX, centerY);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(centerX / 2, centerY);
|
||||
await page.mouse.move(0, centerY);
|
||||
await page.mouse.move(endX, centerY);
|
||||
await page.mouse.up();
|
||||
|
||||
// Add a timeout to make sure the item is open
|
||||
|
||||
Reference in New Issue
Block a user