feat(item-option): add shapes (#29636)
@ -1105,6 +1105,7 @@ ion-item-option,prop,expandable,boolean,false,false,false
|
||||
ion-item-option,prop,href,string | undefined,undefined,false,false
|
||||
ion-item-option,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-item-option,prop,rel,string | undefined,undefined,false,false
|
||||
ion-item-option,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
|
||||
ion-item-option,prop,target,string | undefined,undefined,false,false
|
||||
ion-item-option,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
ion-item-option,prop,type,"button" | "reset" | "submit",'button',false,false
|
||||
|
8
core/src/components.d.ts
vendored
@ -1657,6 +1657,10 @@ export namespace Components {
|
||||
* Specifies the relationship of the target object to the link object. The value is a space-separated list of [link types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types).
|
||||
*/
|
||||
"rel": string | undefined;
|
||||
/**
|
||||
* Set to `"rectangular"` for non-rounded corners. Set to `"soft"` for slightly rounded corners. Set to `"round"` for fully rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
|
||||
*/
|
||||
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||
/**
|
||||
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
|
||||
*/
|
||||
@ -6961,6 +6965,10 @@ declare namespace LocalJSX {
|
||||
* Specifies the relationship of the target object to the link object. The value is a space-separated list of [link types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types).
|
||||
*/
|
||||
"rel"?: string | undefined;
|
||||
/**
|
||||
* Set to `"rectangular"` for non-rounded corners. Set to `"soft"` for slightly rounded corners. Set to `"round"` for fully rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
|
||||
*/
|
||||
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||
/**
|
||||
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
|
||||
*/
|
||||
|
@ -45,3 +45,21 @@
|
||||
min-width: initial;
|
||||
height: globals.$ionic-scale-500;
|
||||
}
|
||||
|
||||
// Item Option Shapes
|
||||
// --------------------------------------------------
|
||||
|
||||
/* Round */
|
||||
:host(.item-option-round) {
|
||||
@include globals.border-radius(globals.$ionic-border-radius-300);
|
||||
}
|
||||
|
||||
/* Soft */
|
||||
:host(.item-option-soft) {
|
||||
@include globals.border-radius(globals.$ionic-border-radius-200);
|
||||
}
|
||||
|
||||
/* Rectangular */
|
||||
:host(.item-option-rectangular) {
|
||||
@include globals.border-radius(globals.$ionic-border-radius-0);
|
||||
}
|
||||
|
@ -80,6 +80,15 @@ export class ItemOption implements ComponentInterface, AnchorInterface, ButtonIn
|
||||
*/
|
||||
@Prop() type: 'submit' | 'reset' | 'button' = 'button';
|
||||
|
||||
/**
|
||||
* Set to `"rectangular"` for non-rounded corners.
|
||||
* Set to `"soft"` for slightly rounded corners.
|
||||
* Set to `"round"` for fully rounded corners.
|
||||
*
|
||||
* Defaults to `"round"` for the `ionic` theme, undefined for all other themes.
|
||||
*/
|
||||
@Prop() shape?: 'soft' | 'round' | 'rectangular';
|
||||
|
||||
private onClick = (ev: Event) => {
|
||||
const el = (ev.target as HTMLElement).closest('ion-item-option');
|
||||
if (el) {
|
||||
@ -87,10 +96,28 @@ export class ItemOption implements ComponentInterface, AnchorInterface, ButtonIn
|
||||
}
|
||||
};
|
||||
|
||||
private getShape(): string | undefined {
|
||||
const theme = getIonTheme(this);
|
||||
const { shape } = this;
|
||||
|
||||
// TODO(ROU-10841): Remove theme check when shapes are defined for all themes.
|
||||
if (theme !== 'ionic') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (shape === undefined) {
|
||||
return 'round';
|
||||
}
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { disabled, expandable, href } = this;
|
||||
const TagType = href === undefined ? 'button' : ('a' as any);
|
||||
const theme = getIonTheme(this);
|
||||
const shape = this.getShape();
|
||||
|
||||
const attrs =
|
||||
TagType === 'button'
|
||||
? { type: this.type }
|
||||
@ -105,6 +132,7 @@ export class ItemOption implements ComponentInterface, AnchorInterface, ButtonIn
|
||||
onClick={this.onClick}
|
||||
class={createColorClasses(this.color, {
|
||||
[theme]: true,
|
||||
[`item-option-${shape}`]: shape !== undefined,
|
||||
'item-option-disabled': disabled,
|
||||
'item-option-expandable': expandable,
|
||||
'ion-activatable': true,
|
||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.9 KiB |
110
core/src/components/item-sliding/test/shapes/index.html
Normal file
@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Item Sliding - Shapes</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<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>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Item Sliding - Shapes</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item-sliding id="default">
|
||||
<ion-item>
|
||||
<ion-label>Default</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="danger">
|
||||
<ion-icon slot="top" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
<ion-item-option color="light">
|
||||
<ion-icon slot="top" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" expandable>
|
||||
<ion-icon slot="top" name="bookmark"></ion-icon>
|
||||
Save
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="round">
|
||||
<ion-item>
|
||||
<ion-label>Round</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option shape="round" color="danger">
|
||||
<ion-icon slot="top" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
<ion-item-option shape="round" color="light">
|
||||
<ion-icon slot="top" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option shape="round" color="secondary" expandable>
|
||||
<ion-icon slot="top" name="bookmark"></ion-icon>
|
||||
Save
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="soft">
|
||||
<ion-item>
|
||||
<ion-label>Soft</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option shape="soft" color="danger">
|
||||
<ion-icon slot="top" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
<ion-item-option shape="soft" color="light">
|
||||
<ion-icon slot="top" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option shape="soft" color="secondary" expandable>
|
||||
<ion-icon slot="top" name="bookmark"></ion-icon>
|
||||
Save
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="rectangular">
|
||||
<ion-item>
|
||||
<ion-label>Rectangular</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option shape="rectangular" color="danger">
|
||||
<ion-icon slot="top" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
<ion-item-option shape="rectangular" color="light">
|
||||
<ion-icon slot="top" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option shape="rectangular" color="secondary" expandable>
|
||||
<ion-icon slot="top" name="bookmark"></ion-icon>
|
||||
Save
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,30 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test, dragElementBy } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* The shapes on the `item-option` do not vary by direction
|
||||
* when they are not being dragged.
|
||||
*/
|
||||
configs({ modes: ['ionic-md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('item-sliding: shapes'), () => {
|
||||
test('should not have visual regressions when not expanded', async ({ page }) => {
|
||||
await page.goto(`/src/components/item-sliding/test/shapes`, config);
|
||||
|
||||
const itemIDs = ['round', 'soft', 'rectangular'];
|
||||
for (const itemID of itemIDs) {
|
||||
const item = page.locator(`#${itemID}`);
|
||||
|
||||
/**
|
||||
* Negative dragByX value to drag element from the right to the left
|
||||
* to reveal the options on the right side.
|
||||
*/
|
||||
const dragByX = -150;
|
||||
|
||||
await dragElementBy(item, page, dragByX);
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(item).toHaveScreenshot(screenshot(`item-sliding-${itemID}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 4.5 KiB |
@ -1108,14 +1108,14 @@ export declare interface IonItemGroup extends Components.IonItemGroup {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'target', 'theme', 'type']
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'shape', 'target', 'theme', 'type']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-item-option',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'target', 'theme', 'type'],
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'shape', 'target', 'theme', 'type'],
|
||||
})
|
||||
export class IonItemOption {
|
||||
protected el: HTMLElement;
|
||||
|
@ -1076,14 +1076,14 @@ export declare interface IonItemGroup extends Components.IonItemGroup {}
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonItemOption,
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'target', 'theme', 'type']
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'shape', 'target', 'theme', 'type']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-item-option',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'target', 'theme', 'type'],
|
||||
inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'shape', 'target', 'theme', 'type'],
|
||||
standalone: true
|
||||
})
|
||||
export class IonItemOption {
|
||||
|
@ -492,7 +492,8 @@ export const IonItemOption = /*@__PURE__*/ defineContainer<JSX.IonItemOption>('i
|
||||
'href',
|
||||
'rel',
|
||||
'target',
|
||||
'type'
|
||||
'type',
|
||||
'shape'
|
||||
]);
|
||||
|
||||
|
||||
|