fix(react): add class based APIs (#16665)

fixes #16583
This commit is contained in:
Manu MA
2018-12-11 01:08:00 +01:00
committed by GitHub
parent 64557a7bcc
commit 2933f61e8d
32 changed files with 117 additions and 67 deletions

View File

@@ -832,7 +832,7 @@ export class IonSpinner {
}
export declare interface IonSplitPane extends StencilComponents<'IonSplitPane'> {}
@Component({ selector: 'ion-split-pane', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '<ng-content></ng-content>', inputs: ['disabled', 'when'] })
@Component({ selector: 'ion-split-pane', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '<ng-content></ng-content>', inputs: ['contentId', 'disabled', 'when'] })
export class IonSplitPane {
ionChange!: EventEmitter<CustomEvent>;
ionSplitPaneVisible!: EventEmitter<CustomEvent>;
@@ -840,7 +840,7 @@ export class IonSplitPane {
constructor(c: ChangeDetectorRef, r: ElementRef) {
c.detach();
const el = r.nativeElement;
proxyInputs(this, el, ['disabled', 'when']);
proxyInputs(this, el, ['contentId', 'disabled', 'when']);
proxyOutputs(this, el, ['ionChange', 'ionSplitPaneVisible']);
}
}

View File

@@ -1018,6 +1018,7 @@ ion-spinner,prop,paused,boolean,false,false
ion-spinner,css-prop,--color
ion-split-pane
ion-split-pane,prop,contentId,string | undefined,undefined,false
ion-split-pane,prop,disabled,boolean,false,false
ion-split-pane,prop,when,boolean | string,QUERY['lg'],false
ion-split-pane,event,ionChange,{visible: boolean},false

View File

@@ -4400,6 +4400,10 @@ export namespace Components {
}
interface IonSplitPane {
/**
* The content `id` of the split-pane's main content. This property can be used instead of the `[main]` attribute to select the `main` content of the split-pane. ```html * <ion-split-pane content-id="my-content"> * <ion-menu></ion-menu> * <div id="my-content"> * </ion-split-pane> * ```
*/
'contentId'?: string;
/**
* If `true`, the split pane will be hidden.
*/
@@ -4410,6 +4414,10 @@ export namespace Components {
'when': string | boolean;
}
interface IonSplitPaneAttributes extends StencilHTMLAttributes {
/**
* The content `id` of the split-pane's main content. This property can be used instead of the `[main]` attribute to select the `main` content of the split-pane. ```html * <ion-split-pane content-id="my-content"> * <ion-menu></ion-menu> * <div id="my-content"> * </ion-split-pane> * ```
*/
'contentId'?: string;
/**
* If `true`, the split pane will be hidden.
*/

View File

@@ -249,7 +249,6 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
{cancelButton &&
<div class="action-sheet-group action-sheet-group-cancel">
<button
ion-activatable
type="button"
class={buttonClass(cancelButton)}
onClick={() => this.buttonClick(cancelButton)}
@@ -275,6 +274,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
function buttonClass(button: ActionSheetButton): CssClassMap {
return {
'action-sheet-button': true,
'ion-activatable': true,
[`action-sheet-${button.role}`]: button.role !== undefined,
...getClassMap(button.cssClass),
};

View File

@@ -424,7 +424,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
return (
<div class={alertButtonGroupClass}>
{buttons.map(button =>
<button type="button" ion-activatable class={buttonClass(button)} tabIndex={0} onClick={() => this.buttonClick(button)}>
<button type="button" class={buttonClass(button)} tabIndex={0} onClick={() => this.buttonClick(button)}>
<span class="alert-button-inner">
{button.text}
</span>
@@ -470,6 +470,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
function buttonClass(button: AlertButton): CssClassMap {
return {
'alert-button': true,
'ion-activatable': true,
...getClassMap(button.cssClass)
};
}

View File

@@ -33,8 +33,10 @@ export class Anchor implements ComponentInterface {
hostData() {
return {
class: createColorClasses(this.color),
'ion-activatable': true
class: {
...createColorClasses(this.color),
'ion-activatable': true
}
};
}

View File

@@ -59,12 +59,11 @@ export class BackButton implements ComponentInterface {
const showBackButton = this.defaultHref !== undefined;
return {
'ion-activatable': true,
class: {
...createColorClasses(this.color),
// ion-buttons target .button
'button': true,
'button': true, // ion-buttons target .button
'ion-activatable': true,
'show-back-button': showBackButton
}
};

View File

@@ -146,7 +146,6 @@ export class Button implements ComponentInterface {
fill = this.inToolbar ? 'clear' : 'solid';
}
return {
'ion-activatable': true,
'aria-disabled': this.disabled ? 'true' : null,
class: {
...createColorClasses(color),
@@ -158,7 +157,8 @@ export class Button implements ComponentInterface {
[`${buttonType}-strong`]: strong,
'focused': keyFocus,
'button-disabled': disabled
'button-disabled': disabled,
'ion-activatable': true,
}
};
}

View File

@@ -31,10 +31,10 @@ export class Chip implements ComponentInterface {
hostData() {
return {
'ion-activatable': true,
class: {
...createColorClasses(this.color),
'chip-outline': this.outline
'chip-outline': this.outline,
'ion-activatable': true,
}
};
}

View File

@@ -93,7 +93,6 @@ export class FabButton implements ComponentInterface {
hostData() {
const inList = hostContext('ion-fab-list', this.el);
return {
'ion-activatable': true,
'aria-disabled': this.disabled ? 'true' : null,
class: {
...createColorClasses(this.color),
@@ -103,7 +102,8 @@ export class FabButton implements ComponentInterface {
'fab-button-show': this.show,
'fab-button-disabled': this.disabled,
'fab-button-translucent': this.translucent,
'focused': this.keyFocus
'ion-activatable': true,
'focused': this.keyFocus,
}
};
}

View File

@@ -50,10 +50,10 @@ export class ItemOption implements ComponentInterface {
hostData() {
return {
'ion-activatable': true,
class: {
...createColorClasses(this.color),
'item-option-expandable': this.expandable,
'ion-activatable': true,
}
};
}

View File

@@ -127,7 +127,6 @@ export class Item implements ComponentInterface {
});
return {
'ion-activatable': this.isClickable(),
'aria-disabled': this.disabled ? 'true' : null,
class: {
...childStyles,
@@ -136,7 +135,8 @@ export class Item implements ComponentInterface {
'item-disabled': this.disabled,
'in-list': hostContext('ion-list', this.el),
'item': true,
'item-multiple-inputs': this.multipleInputs
'item-multiple-inputs': this.multipleInputs,
'ion-activatable': this.isClickable(),
}
};
}

View File

@@ -38,10 +38,9 @@ export class MenuButton implements ComponentInterface {
hostData() {
return {
'ion-activatable': true,
class: {
// ion-buttons target .button
'button': true
'button': true, // ion-buttons target .button
'ion-activatable': true,
}
};
}

View File

@@ -160,9 +160,7 @@ export class Menu implements ComponentInterface, MenuI {
if (!content || !content.tagName) {
// requires content element
console.error(
'Menu: must have a "content" element to listen for drag events on.'
);
console.error('Menu: must have a "content" element to listen for drag events on.');
return;
}
this.contentEl = content as HTMLElement;

View File

@@ -31,7 +31,7 @@ These can be controlled from the templates, or programmatically using the MenuCo
```html
<ion-app>
<ion-menu side="start">
<ion-menu side="start" content-id="menu-content">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Left Menu</ion-title>
@@ -39,7 +39,7 @@ These can be controlled from the templates, or programmatically using the MenuCo
</ion-header>
</ion-menu>
<ion-menu side="end">
<ion-menu side="end" content-id="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Hola</ion-title>
@@ -51,7 +51,7 @@ These can be controlled from the templates, or programmatically using the MenuCo
</ion-content>
</ion-menu>
<div class="ion-page" main>
<div class="ion-page" id="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu - Basic</ion-title>

View File

@@ -1,6 +1,6 @@
```html
<ion-app>
<ion-menu side="start">
<ion-menu side="start" content-id="menu-content">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Left Menu</ion-title>
@@ -8,7 +8,7 @@
</ion-header>
</ion-menu>
<ion-menu side="end">
<ion-menu side="end" content-id="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Hola</ion-title>
@@ -20,7 +20,7 @@
</ion-content>
</ion-menu>
<div class="ion-page" main>
<div class="ion-page" id="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu - Basic</ion-title>

View File

@@ -256,7 +256,6 @@ export class Picker implements ComponentInterface, OverlayInterface {
<div class={buttonWrapperClass(b)}>
<button
type="button"
ion-activatable
onClick={() => this.buttonClick(b)}
class={buttonClass(b)}
>
@@ -286,6 +285,7 @@ function buttonWrapperClass(button: PickerButton): CssClassMap {
function buttonClass(button: PickerButton): CssClassMap {
return {
'picker-button': true,
'ion-activatable': true,
...getClassMap(button.cssClass)
};
}

View File

@@ -56,23 +56,23 @@
<p>
<ion-button size="large" fill="clear">Large</ion-button>
</p>
<div class="my-block" ion-activatable>
<div class="my-block ion-activatable">
<ion-ripple-effect></ion-ripple-effect>
This is just a div + effect behind
<ion-button onclick="buttonClicked()">Nested button</ion-button>
</div>
<div class="my-block" ion-activatable>
<div class="my-block ion-activatable">
This is just a div + effect on top
<ion-button onclick="buttonClicked()">Nested button</ion-button>
<ion-ripple-effect></ion-ripple-effect>
</div>
<div class="my-block" ion-activatable>
<div class="my-block ion-activatable">
This is just a div + effect
<ion-ripple-effect></ion-ripple-effect>
</div>
<a class="my-block" ion-activatable>
<a class="my-block ion-activatable">
This is just a a + effect on top
<ion-button onclick="buttonClicked()">Nested button</ion-button>
<ion-ripple-effect></ion-ripple-effect>
@@ -84,7 +84,7 @@
<ion-ripple-effect></ion-ripple-effect>
</button>
<a class="block" ion-activatable>
<a class="block ion-activatable">
<ion-ripple-effect></ion-ripple-effect>
</a>
</ion-content>

View File

@@ -68,7 +68,6 @@ export class SegmentButton implements ComponentInterface {
hostData() {
const { checked, disabled, hasIcon, hasLabel, layout } = this;
return {
'ion-activatable': 'instant',
'aria-disabled': disabled ? 'true' : null,
class: {
'segment-button-has-label': hasLabel,
@@ -77,7 +76,9 @@ export class SegmentButton implements ComponentInterface {
'segment-button-has-icon-only': hasIcon && !hasLabel,
'segment-button-disabled': disabled,
'segment-button-checked': checked,
[`segment-button-layout-${layout}`]: true
[`segment-button-layout-${layout}`]: true,
'ion-activatable': true,
'ion-activatable-instant': true,
}
};
}

View File

@@ -45,9 +45,9 @@ SplitPane also provides some predefined media queries that can be used.
### Angular
```html
<ion-split-pane>
<ion-split-pane contentId="menu-content">
<!-- our side menu -->
<ion-menu>
<ion-menu contentId="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
@@ -56,7 +56,7 @@ SplitPane also provides some predefined media queries that can be used.
</ion-menu>
<!-- the main content -->
<ion-router-outlet main></ion-router-outlet>
<ion-router-outlet id="menu-content"></ion-router-outlet>
</ion-split-pane>
```
@@ -64,9 +64,9 @@ SplitPane also provides some predefined media queries that can be used.
### Javascript
```html
<ion-split-pane>
<ion-split-pane content-id="menu-content">
<!-- our side menu -->
<ion-menu>
<ion-menu content-id="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
@@ -75,7 +75,7 @@ SplitPane also provides some predefined media queries that can be used.
</ion-menu>
<!-- the main content -->
<ion-content main>
<ion-content id="menu-content">
<h1>Hello</h1>
</ion-content>
</ion-split-pane>
@@ -85,10 +85,11 @@ SplitPane also provides some predefined media queries that can be used.
## Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- |
| `disabled` | `disabled` | If `true`, the split pane will be hidden. | `boolean` | `false` |
| `when` | `when` | When the split-pane should be shown. Can be a CSS media query expression, or a shortcut expression. Can also be a boolean expression. | `boolean \| string` | `QUERY['lg']` |
| Property | Attribute | Description | Type | Default |
| ----------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------------- |
| `contentId` | `content-id` | The content `id` of the split-pane's main content. This property can be used instead of the `[main]` attribute to select the `main` content of the split-pane. ```html * <ion-split-pane content-id="my-content"> * <ion-menu></ion-menu> * <div id="my-content"> * </ion-split-pane> * ``` | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the split pane will be hidden. | `boolean` | `false` |
| `when` | `when` | When the split-pane should be shown. Can be a CSS media query expression, or a shortcut expression. Can also be a boolean expression. | `boolean \| string` | `QUERY['lg']` |
## Events

View File

@@ -33,6 +33,21 @@ export class SplitPane implements ComponentInterface {
@Prop({ context: 'isServer' }) isServer!: boolean;
@Prop({ context: 'window' }) win!: Window;
/**
* The content `id` of the split-pane's main content.
* This property can be used instead of the `[main]` attribute to select the `main`
* content of the split-pane.
*
* ```html
* <ion-split-pane content-id="my-content">
* <ion-menu></ion-menu>
* <div id="my-content">
* </ion-split-pane>
* ```
*
*/
@Prop() contentId?: string;
/**
* If `true`, the split pane will be hidden.
*/
@@ -129,12 +144,13 @@ export class SplitPane implements ComponentInterface {
if (this.isServer) {
return;
}
const contentId = this.contentId;
const children = this.el.children;
const nu = this.el.childElementCount;
let foundMain = false;
for (let i = 0; i < nu; i++) {
const child = children[i] as HTMLElement;
const isMain = child.hasAttribute('main');
const isMain = contentId !== undefined ? child.id === contentId : child.hasAttribute('main');
if (isMain) {
if (foundMain) {
console.warn('split pane can not have more than one main node');

View File

@@ -12,9 +12,9 @@
<body>
<ion-app>
<ion-split-pane id="splitPane">
<ion-split-pane id="splitPane" content-id="split-content">
<ion-menu side="start">
<ion-menu side="start" content-id="split-content">
<ion-header>
<ion-toolbar color="secondary">
@@ -47,7 +47,7 @@
</ion-menu>
<div class='ion-page' main>
<div class='ion-page' id="split-content">
<ion-header>
<ion-toolbar>

View File

@@ -1,7 +1,7 @@
```html
<ion-split-pane>
<ion-split-pane contentId="menu-content">
<!-- our side menu -->
<ion-menu>
<ion-menu contentId="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
@@ -10,6 +10,6 @@
</ion-menu>
<!-- the main content -->
<ion-router-outlet main></ion-router-outlet>
<ion-router-outlet id="menu-content"></ion-router-outlet>
</ion-split-pane>
```

View File

@@ -1,7 +1,7 @@
```html
<ion-split-pane>
<ion-split-pane content-id="menu-content">
<!-- our side menu -->
<ion-menu>
<ion-menu content-id="menu-content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
@@ -10,7 +10,7 @@
</ion-menu>
<!-- the main content -->
<ion-content main>
<ion-content id="menu-content">
<h1>Hello</h1>
</ion-content>
</ion-split-pane>

View File

@@ -91,7 +91,6 @@ export class TabButton implements ComponentInterface {
const { disabled, hasIcon, hasLabel, layout, selected, tab } = this;
return {
'role': 'tab',
'ion-activatable': true,
'aria-selected': selected ? 'true' : null,
'id': `tab-button-${tab}`,
'aria-controls': `tab-view-${tab}`,
@@ -103,6 +102,7 @@ export class TabButton implements ComponentInterface {
'tab-has-label-only': hasLabel && !hasIcon,
'tab-has-icon-only': hasIcon && !hasLabel,
[`tab-layout-${layout}`]: true,
'ion-activatable': true,
}
};
}

View File

@@ -207,7 +207,7 @@ export class Toast implements ComponentInterface, OverlayInterface {
<div class="toast-message">{this.message}</div>
}
{this.showCloseButton &&
<ion-button fill="clear" ion-activatable class="toast-button" onClick={() => this.dismiss(undefined, 'cancel')}>
<ion-button fill="clear" class="toast-button ion-activatable" onClick={() => this.dismiss(undefined, 'cancel')}>
{this.closeButtonText || 'Close'}
</ion-button>
}

View File

@@ -145,6 +145,7 @@ button div,
button span,
button ion-icon,
button ion-label,
.ion-tappable,
[tappable],
[tappable] div,
[tappable] span,

View File

@@ -13,6 +13,7 @@ $margin: var(--ion-margin, 16px);
// Padding
// --------------------------------------------------
.ion-no-padding,
[no-padding] {
--padding-start: 0;
--padding-end: 0;
@@ -22,6 +23,7 @@ $margin: var(--ion-margin, 16px);
@include padding(0);
}
.ion-padding,
[padding] {
--padding-start: #{$padding};
--padding-end: #{$padding};
@@ -31,30 +33,35 @@ $margin: var(--ion-margin, 16px);
@include padding($padding);
}
.ion-padding-top,
[padding-top] {
--padding-top: #{$padding};
@include padding($padding, null, null, null);
}
.ion-padding-start,
[padding-start] {
--padding-start: #{$padding};
@include padding-horizontal($padding, null);
}
.ion-padding-end,
[padding-end] {
--padding-end: #{$padding};
@include padding-horizontal(null, $padding);
}
.ion-padding-bottom,
[padding-bottom] {
--padding-bottom: #{$padding};
@include padding(null, null, $padding, null);
}
.ion-padding-vertical,
[padding-vertical] {
--padding-top: #{$padding};
--padding-bottom: #{$padding};
@@ -62,6 +69,7 @@ $margin: var(--ion-margin, 16px);
@include padding($padding, null, $padding, null);
}
.ion-padding-horizontal,
[padding-horizontal] {
--padding-start: #{$padding};
--padding-end: #{$padding};
@@ -73,6 +81,7 @@ $margin: var(--ion-margin, 16px);
// Margin
// --------------------------------------------------
.ion-no-margin,
[no-margin] {
--margin-start: 0;
--margin-end: 0;
@@ -82,6 +91,7 @@ $margin: var(--ion-margin, 16px);
@include margin(0);
}
.ion-margin,
[margin] {
--margin-start: #{$margin};
--margin-end: #{$margin};
@@ -91,31 +101,35 @@ $margin: var(--ion-margin, 16px);
@include margin($margin);
}
.ion-margin-top,
[margin-top] {
--margin-top: #{$margin};
@include margin($margin, null, null, null);
}
.ion-margin-start,
[margin-start] {
--margin-start: #{$margin};
@include margin-horizontal($margin, null);
}
.ion-margin-end,
[margin-end] {
--margin-end: #{$margin};
@include margin-horizontal(null, $margin);
}
.ion-margin-bottom,
[margin-bottom] {
--margin-bottom: #{$margin};
@include margin(null, null, $margin, null);
}
.ion-margin-vertical,
[margin-vertical] {
--margin-top: #{$margin};
--margin-bottom: #{$margin};
@@ -123,6 +137,7 @@ $margin: var(--ion-margin, 16px);
@include margin($margin, null, $margin, null);
}
.ion-margin-horizontal,
[margin-horizontal] {
--margin-start: #{$margin};
--margin-end: #{$margin};

View File

@@ -11,34 +11,42 @@
@include media-breakpoint-up($breakpoint, $screen-breakpoints) {
// Provide `[text-{bp}]` attributes for aligning the text based
// on the breakpoint
.ion-text#{$infix}-center,
[text#{$infix}-center] {
text-align: center !important;
}
.ion-text#{$infix}-justify,
[text#{$infix}-justify] {
text-align: justify !important;
}
.ion-text#{$infix}-start,
[text#{$infix}-start] {
text-align: start !important;
}
.ion-text#{$infix}-end,
[text#{$infix}-end] {
text-align: end !important;
}
.ion-text#{$infix}-left,
[text#{$infix}-left] {
text-align: left !important;
}
.ion-text#{$infix}-right,
[text#{$infix}-right] {
text-align: right !important;
}
.ion-text#{$infix}-nowrap,
[text#{$infix}-nowrap] {
white-space: nowrap !important;
}
.ion-text#{$infix}-wrap,
[text#{$infix}-wrap] {
white-space: normal !important;
}

View File

@@ -11,16 +11,19 @@
@include media-breakpoint-up($breakpoint, $screen-breakpoints) {
// Provide `[text-{bp}]` attributes for transforming the text based
// on the breakpoint
.ion-text#{$infix}-uppercase,
[text#{$infix}-uppercase] {
/* stylelint-disable-next-line declaration-no-important */
text-transform: uppercase !important;
}
.ion-text#{$infix}-lowercase,
[text#{$infix}-lowercase] {
/* stylelint-disable-next-line declaration-no-important */
text-transform: lowercase !important;
}
.ion-text#{$infix}-capitalize,
[text#{$infix}-capitalize] {
/* stylelint-disable-next-line declaration-no-important */
text-transform: capitalize !important;

View File

@@ -58,9 +58,6 @@ declare global {
interface StencilGlobalHTMLAttributes {
// for ion-menu and ion-split-pane
main?: boolean;
tappable?: boolean;
'ion-activatable'?: boolean;
padding?: boolean;
['padding-top']?: boolean;
['padding-bottom']?: boolean;

View File

@@ -167,17 +167,17 @@ function getActivatableTarget(ev: any): any {
const path = ev.composedPath() as HTMLElement[];
for (let i = 0; i < path.length - 2; i++) {
const el = path[i];
if (el.hasAttribute && el.hasAttribute('ion-activatable')) {
if (el.classList && el.classList.contains('ion-activatable')) {
return el;
}
}
} else {
return ev.target.closest('[ion-activatable]');
return ev.target.closest('.ion-activatable');
}
}
function isInstant(el: HTMLElement) {
return el.getAttribute('ion-activatable') === 'instant';
return el.classList.contains('ion-activatable-instant');
}
function getRippleEffect(el: HTMLElement) {