mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
fix(all): accesibility and global styles for hidden nodes
This commit is contained in:
@ -65,7 +65,6 @@
|
|||||||
"lint.ts": "tslint --project .",
|
"lint.ts": "tslint --project .",
|
||||||
"lint.ts.fix": "tslint --project . --fix",
|
"lint.ts.fix": "tslint --project . --fix",
|
||||||
"prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next",
|
"prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next",
|
||||||
"set.version": "node scripts/set-version.js",
|
|
||||||
"snapshot": "node ./scripts/e2e --snapshot",
|
"snapshot": "node ./scripts/e2e --snapshot",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test.watch": "jest --watch --no-cache",
|
"test.watch": "jest --watch --no-cache",
|
||||||
|
@ -26,13 +26,11 @@ ion-app,
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide-page {
|
.ion-page-invisible {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[hidden] {
|
.ion-page-hidden {
|
||||||
// TODO can we remove important here or remove this entirely
|
|
||||||
// since it is also in structure.scss
|
|
||||||
/* stylelint-disable-next-line declaration-no-important */
|
/* stylelint-disable-next-line declaration-no-important */
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
:host {
|
:host {
|
||||||
--ion-color-base: #{$background-color};
|
--ion-color-base: #{$background-color};
|
||||||
--ion-color-contrast: #{$text-color};
|
--ion-color-contrast: #{$text-color};
|
||||||
--padding-top: 0;
|
--padding-top: 0px;
|
||||||
--padding-bottom: 0;
|
--padding-bottom: 0px;
|
||||||
--padding-start: 0;
|
--padding-start: 0px;
|
||||||
--padding-end: 0;
|
--padding-end: 0px;
|
||||||
--keyboard-offset: 0;
|
--keyboard-offset: 0px;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
4
core/src/components/menu-toggle/menu-toggle.scss
Normal file
4
core/src/components/menu-toggle/menu-toggle.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
:host(.menu-toggle-hidden) {
|
||||||
|
/* stylelint-disable-next-line declaration-no-important */
|
||||||
|
display: none;
|
||||||
|
}
|
@ -2,6 +2,8 @@ import { Component, Listen, Prop, State } from '@stencil/core';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-menu-toggle',
|
tag: 'ion-menu-toggle',
|
||||||
|
styleUrl: 'menu-toggle.scss',
|
||||||
|
shadow: true
|
||||||
})
|
})
|
||||||
export class MenuToggle {
|
export class MenuToggle {
|
||||||
@Prop({ context: 'document' })
|
@Prop({ context: 'document' })
|
||||||
@ -54,9 +56,16 @@ export class MenuToggle {
|
|||||||
hostData() {
|
hostData() {
|
||||||
const hidden = this.autoHide && !this.visible;
|
const hidden = this.autoHide && !this.visible;
|
||||||
return {
|
return {
|
||||||
'hidden': hidden
|
'aria-hidden': hidden ? 'true' : null,
|
||||||
|
class: {
|
||||||
|
'menu-toggle-hidden': hidden,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <slot></slot>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMenuController(doc: Document): Promise<HTMLIonMenuControllerElement|undefined> {
|
function getMenuController(doc: Document): Promise<HTMLIonMenuControllerElement|undefined> {
|
||||||
|
@ -2,7 +2,7 @@ import { Build, Component, Element, Event, EventEmitter, Method, Prop, QueueApi,
|
|||||||
import { ViewLifecycle } from '../..';
|
import { ViewLifecycle } from '../..';
|
||||||
import { Animation, ComponentProps, Config, FrameworkDelegate, GestureDetail, Mode, NavComponent, NavOptions, NavOutlet, NavResult, RouteID, RouteWrite, TransitionDoneFn, TransitionInstruction, ViewController } from '../../interface';
|
import { Animation, ComponentProps, Config, FrameworkDelegate, GestureDetail, Mode, NavComponent, NavOptions, NavOutlet, NavResult, RouteID, RouteWrite, TransitionDoneFn, TransitionInstruction, ViewController } from '../../interface';
|
||||||
import { assert } from '../../utils/helpers';
|
import { assert } from '../../utils/helpers';
|
||||||
import { TransitionOptions, lifecycle, transition } from '../../utils/transition';
|
import { TransitionOptions, lifecycle, setPageHidden, transition } from '../../utils/transition';
|
||||||
import { ViewState, convertToViews, matches } from './view-controller';
|
import { ViewState, convertToViews, matches } from './view-controller';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -850,15 +850,16 @@ export class Nav implements NavOutlet {
|
|||||||
|
|
||||||
for (let i = views.length - 1; i >= 0; i--) {
|
for (let i = views.length - 1; i >= 0; i--) {
|
||||||
const view = views[i];
|
const view = views[i];
|
||||||
|
const element = view.element;
|
||||||
if (i > activeViewIndex) {
|
if (i > activeViewIndex) {
|
||||||
// this view comes after the active view
|
// this view comes after the active view
|
||||||
// let's unload it
|
// let's unload it
|
||||||
lifecycle(this.win, view.element, ViewLifecycle.WillUnload);
|
lifecycle(this.win, element, ViewLifecycle.WillUnload);
|
||||||
this.destroyView(view);
|
this.destroyView(view);
|
||||||
} else if (i < activeViewIndex) {
|
} else if (i < activeViewIndex) {
|
||||||
// this view comes before the active view
|
// this view comes before the active view
|
||||||
// and it is not a portal then ensure it is hidden
|
// and it is not a portal then ensure it is hidden
|
||||||
view.element!.hidden = true;
|
setPageHidden(element!, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ export class ViewController {
|
|||||||
|
|
||||||
if (!this.element) {
|
if (!this.element) {
|
||||||
const component = this.component;
|
const component = this.component;
|
||||||
this.element = await attachComponent(this.delegate, container, component, ['ion-page', 'hide-page'], this.params);
|
this.element = await attachComponent(this.delegate, container, component, ['ion-page', 'ion-page-invisible'], this.params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ export class RouterOutlet implements NavOutlet {
|
|||||||
this.activeComponent = component;
|
this.activeComponent = component;
|
||||||
|
|
||||||
// attach entering view to DOM
|
// attach entering view to DOM
|
||||||
const enteringEl = await attachComponent(this.delegate, this.el, component, ['ion-page', 'hide-page'], params);
|
const enteringEl = await attachComponent(this.delegate, this.el, component, ['ion-page', 'ion-page-invisible'], params);
|
||||||
const leavingEl = this.activeEl;
|
const leavingEl = this.activeEl;
|
||||||
|
|
||||||
// commit animation
|
// commit animation
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:host(.tab-hidden) {
|
:host(.tab-hidden) {
|
||||||
display: none;
|
/* stylelint-disable-next-line declaration-no-important */
|
||||||
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
4
core/src/components/tab/tab.scss
Normal file
4
core/src/components/tab/tab.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
:host(.tab-hidden) {
|
||||||
|
/* stylelint-disable-next-line declaration-no-important */
|
||||||
|
display: none !important;
|
||||||
|
}
|
@ -3,7 +3,9 @@ import { Color, ComponentRef, FrameworkDelegate } from '../../interface';
|
|||||||
import { attachComponent } from '../../utils/framework-delegate';
|
import { attachComponent } from '../../utils/framework-delegate';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-tab'
|
tag: 'ion-tab',
|
||||||
|
styleUrl: 'tab.scss',
|
||||||
|
shadow: true
|
||||||
})
|
})
|
||||||
export class Tab {
|
export class Tab {
|
||||||
|
|
||||||
@ -136,13 +138,19 @@ export class Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostData() {
|
hostData() {
|
||||||
|
const { btnId, active, component } = this;
|
||||||
return {
|
return {
|
||||||
'aria-labelledby': this.btnId,
|
'aria-labelledby': btnId,
|
||||||
|
'aria-hidden': !active ? 'true' : null,
|
||||||
'role': 'tabpanel',
|
'role': 'tabpanel',
|
||||||
'hidden': !this.active,
|
|
||||||
'class': {
|
'class': {
|
||||||
'ion-page': !this.component
|
'ion-page': !component,
|
||||||
|
'tab-hidden': !active
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <slot></slot>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:host(.tabbar-hidden) {
|
:host(.tabbar-hidden) {
|
||||||
display: none;
|
/* stylelint-disable-next-line declaration-no-important */
|
||||||
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(.placement-top) {
|
:host(.placement-top) {
|
||||||
|
@ -24,7 +24,7 @@ export class Tabbar {
|
|||||||
|
|
||||||
@State() canScrollLeft = false;
|
@State() canScrollLeft = false;
|
||||||
@State() canScrollRight = false;
|
@State() canScrollRight = false;
|
||||||
@State() hidden = false;
|
@State() keyboardVisible = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the layout of the text and icon in the tabbar. Available options: `"icon-top"`, `"icon-start"`, `"icon-end"`, `"icon-bottom"`, `"icon-hide"`, `"label-hide"`.
|
* Set the layout of the text and icon in the tabbar. Available options: `"icon-top"`, `"icon-start"`, `"icon-end"`, `"icon-bottom"`, `"icon-hide"`, `"label-hide"`.
|
||||||
@ -68,13 +68,13 @@ export class Tabbar {
|
|||||||
|
|
||||||
@Listen('body:keyboardWillHide')
|
@Listen('body:keyboardWillHide')
|
||||||
protected onKeyboardWillHide() {
|
protected onKeyboardWillHide() {
|
||||||
setTimeout(() => this.hidden = false, 50);
|
setTimeout(() => this.keyboardVisible = false, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Listen('body:keyboardWillShow')
|
@Listen('body:keyboardWillShow')
|
||||||
protected onKeyboardWillShow() {
|
protected onKeyboardWillShow() {
|
||||||
if (this.placement === 'bottom') {
|
if (this.placement === 'bottom') {
|
||||||
this.hidden = true;
|
this.keyboardVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,15 +182,17 @@ export class Tabbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostData() {
|
hostData() {
|
||||||
|
const { color, translucent, layout, placement, keyboardVisible, scrollable } = this;
|
||||||
return {
|
return {
|
||||||
role: 'tablist',
|
role: 'tablist',
|
||||||
|
'aria-hidden': keyboardVisible ? 'true' : null,
|
||||||
class: {
|
class: {
|
||||||
...createColorClasses(this.color),
|
...createColorClasses(color),
|
||||||
'tabbar-translucent': this.translucent,
|
'tabbar-translucent': translucent,
|
||||||
[`layout-${this.layout}`]: true,
|
[`layout-${layout}`]: true,
|
||||||
[`placement-${this.placement}`]: true,
|
[`placement-${placement}`]: true,
|
||||||
'tabbar-hidden': this.hidden,
|
'tabbar-hidden': keyboardVisible,
|
||||||
'scrollable': this.scrollable
|
'scrollable': scrollable
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -209,6 +211,7 @@ export class Tabbar {
|
|||||||
selected={selectedTab === tab}
|
selected={selectedTab === tab}
|
||||||
mode={this.mode}
|
mode={this.mode}
|
||||||
color={this.color}
|
color={this.color}
|
||||||
|
aria-hidden={ !tab.show ? 'true' : null }
|
||||||
class={{ 'tab-hidden': !tab.show }}
|
class={{ 'tab-hidden': !tab.show }}
|
||||||
onClick={(ev) => {
|
onClick={(ev) => {
|
||||||
if (!tab.disabled) {
|
if (!tab.disabled) {
|
||||||
|
@ -50,8 +50,3 @@ body {
|
|||||||
|
|
||||||
text-size-adjust: none;
|
text-size-adjust: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[hidden] {
|
|
||||||
/* stylelint-disable-next-line declaration-no-important */
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
@ -27,7 +27,7 @@ export function iosTransitionAnimation(Animation: Animation, navEl: HTMLElement,
|
|||||||
.addElement(enteringEl)
|
.addElement(enteringEl)
|
||||||
.duration(opts.duration || DURATION)
|
.duration(opts.duration || DURATION)
|
||||||
.easing(opts.easing || EASING)
|
.easing(opts.easing || EASING)
|
||||||
.beforeRemoveClass('hide-page');
|
.beforeRemoveClass('ion-page-invisible');
|
||||||
|
|
||||||
if (leavingEl && navEl) {
|
if (leavingEl && navEl) {
|
||||||
const navDecor = new Animation();
|
const navDecor = new Animation();
|
||||||
|
@ -14,7 +14,7 @@ export function mdTransitionAnimation(Animation: Animation, _: HTMLElement, opts
|
|||||||
const rootTransition = new Animation();
|
const rootTransition = new Animation();
|
||||||
rootTransition
|
rootTransition
|
||||||
.addElement(ionPageElement)
|
.addElement(ionPageElement)
|
||||||
.beforeRemoveClass('hide-page');
|
.beforeRemoveClass('ion-page-invisible');
|
||||||
|
|
||||||
const backDirection = (opts.direction === 'back');
|
const backDirection = (opts.direction === 'back');
|
||||||
if (enteringEl) {
|
if (enteringEl) {
|
||||||
|
@ -45,9 +45,19 @@ function beforeTransition(opts: TransitionOptions) {
|
|||||||
} else {
|
} else {
|
||||||
enteringEl.classList.remove('can-go-back');
|
enteringEl.classList.remove('can-go-back');
|
||||||
}
|
}
|
||||||
enteringEl.hidden = false;
|
setPageHidden(enteringEl, false);
|
||||||
if (leavingEl) {
|
if (leavingEl) {
|
||||||
leavingEl.hidden = false;
|
setPageHidden(leavingEl, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setPageHidden(el: HTMLElement, hidden: boolean) {
|
||||||
|
if (hidden) {
|
||||||
|
el.setAttribute('aria-hidden', 'true');
|
||||||
|
el.classList.add('ion-page-hidden');
|
||||||
|
} else {
|
||||||
|
el.removeAttribute('aria-hidden');
|
||||||
|
el.classList.remove('ion-page-hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,10 +78,10 @@ async function noAnimation(opts: TransitionOptions): Promise<null> {
|
|||||||
const enteringEl = opts.enteringEl;
|
const enteringEl = opts.enteringEl;
|
||||||
const leavingEl = opts.leavingEl;
|
const leavingEl = opts.leavingEl;
|
||||||
if (enteringEl) {
|
if (enteringEl) {
|
||||||
enteringEl.classList.remove('hide-page');
|
enteringEl.classList.remove('ion-page-invisible');
|
||||||
}
|
}
|
||||||
if (leavingEl) {
|
if (leavingEl) {
|
||||||
leavingEl.classList.remove('hide-page');
|
leavingEl.classList.remove('ion-page-invisible');
|
||||||
}
|
}
|
||||||
await waitForReady(opts, false);
|
await waitForReady(opts, false);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user