mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-02 19:12:40 +08:00
chore: a11y polish (#9259)
This commit is contained in:
@ -183,7 +183,19 @@ components that have the btn class name.
|
||||
margin-bottom: 5
|
||||
}
|
||||
|
||||
.a11y-item {
|
||||
.a11y-demo-page .view-item {
|
||||
margin-bottom: 12;
|
||||
font-size: 18;
|
||||
}
|
||||
|
||||
.a11y-demo-page .a11y {
|
||||
a11y-enabled: true;
|
||||
}
|
||||
|
||||
.a11y-demo-page .a11y-role-image {
|
||||
a11y-role: image;
|
||||
}
|
||||
|
||||
.a11y-demo-page .a11y-state-checked {
|
||||
a11y-state: checked;
|
||||
}
|
||||
@ -1,8 +1,34 @@
|
||||
import { Observable, EventData, Page } from '@nativescript/core';
|
||||
import { Observable, EventData, Page, Switch, AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, ShowModalOptions } from '@nativescript/core';
|
||||
|
||||
let page: Page;
|
||||
|
||||
export function navigatingTo(args: EventData) {
|
||||
const page = <Page>args.object;
|
||||
page = <Page>args.object;
|
||||
page.bindingContext = new AccessibilityModel();
|
||||
page.onAccessibilityPerformEscape = () => {
|
||||
console.log('onAccessibilityPerformEscape');
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
export class AccessibilityModel extends Observable {}
|
||||
export class AccessibilityModel extends Observable {
|
||||
labelText = 'Label change on Switch:';
|
||||
switchCheckedText = this.labelText;
|
||||
accessibilityLiveRegions = AccessibilityLiveRegion;
|
||||
accessibilityRole = AccessibilityRole;
|
||||
accessibilityState = AccessibilityState;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
checkedChange(args) {
|
||||
const checked = (args.object as Switch).checked;
|
||||
console.log(checked);
|
||||
this.set('switchCheckedText', `${this.labelText} ${checked}`);
|
||||
}
|
||||
|
||||
openModal() {
|
||||
page.showModal('pages/sample-modal');
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,14 +4,32 @@
|
||||
</ActionBar>
|
||||
</Page.actionBar>
|
||||
|
||||
<GridLayout padding="20">
|
||||
<GridLayout padding="20" class="a11y-demo-page">
|
||||
<ScrollView>
|
||||
<StackLayout>
|
||||
<Label text="Accessible Label" class="a11y-item text-center"></Label>
|
||||
<Button text="Accessible Button" class="a11y-item"></Button>
|
||||
<!-- <Label text="Accessible Label" accessible="true" class="a11y-item text-center"></Label>
|
||||
<Button text="Accessible Button" accessible="true" class="a11y-item"></Button> -->
|
||||
<Label text="Accessible Label" class="view-item a11y text-center" accessibilityLabel="Accessible Label" accessibilityHint="Just a label" accessibilityRole="{{accessibilityRole.StaticText}}" accessibilityValue="Accessible Label" />
|
||||
<Button text="Accessible Button" class="view-item a11y" accessibilityLabel="Accessible Button" accessibilityHint="Tapping this really does nothing" />
|
||||
|
||||
<Image src="res://icon" width="50" class="view-item a11y" accessibilityLabel="Image with explicit attribute role" accessibilityRole="{{accessibilityRole.Image}}" />
|
||||
<Image src="res://icon" width="50" class="view-item a11y a11y-role-image" accessibilityLabel="Image with css defined role" />
|
||||
|
||||
<Switch checked="true" class="view-item a11y" accessibilityLabel="Switch with attribute state" accessibilityState="{{accessibilityState.Checked}}" checkedChange="{{checkedChange}}" />
|
||||
<Switch checked="true" class="view-item a11y a11y-state-checked" accessibilityLabel="Switch with css state" checkedChange="{{checkedChange}}" />
|
||||
|
||||
<TextView hint="TextView" text="{{switchCheckedText}}" class="view-item a11y" accessibilityLabel="TestView with a value" accessibilityLiveRegion="{{accessibilityLiveRegions.Polite}}"/>
|
||||
<TextField hint="TextField" class="view-item a11y" accessibilityLabel="Plain jane TextField" accessibilityHint="Tell us your real name Jane"/>
|
||||
<TextView hint="TextView" class="view-item a11y" accessibilityLabel="Nice TextView" accessibilityHint="Tell us about yourself Jane"/>
|
||||
<GridLayout rows="25" columns="*" class="view-item" accessibilityLabel="No can go GridLayout" accessibilityHint="A grid that will not get bigger when increasing accessible text size">
|
||||
<Label text="IN-Accessible Grid" class="view-item text-center" />
|
||||
</GridLayout>
|
||||
<GridLayout rows="25,25" columns="*,50" class="view-item a11y" accessibilityLabel="Yes an accessible GridLayout" accessibilityHint="A grid that WILL get bigger dynamically when increasing accessible text size">
|
||||
<Label text="Accessible Grid" class="view-item text-center" />
|
||||
<Label row="1" text="With another item in a row" class="view-item text-center" />
|
||||
<Label rowSpan="2" col="1" text="Hi" />
|
||||
</GridLayout>
|
||||
<Button text="Open Modal" class="view-item" tap="{{openModal}}" />
|
||||
<Slider value="10" minValue="0" maxValue="100" class="view-item a11y" accessibilityLabel="Slider" accessibilityHint="A smooth slider" accessibilityValue="10"/>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</GridLayout>
|
||||
</Page>
|
||||
</Page>
|
||||
|
||||
23
apps/toolbox/src/pages/sample-modal.ts
Normal file
23
apps/toolbox/src/pages/sample-modal.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Page, ShownModallyData, Observable } from '@nativescript/core';
|
||||
|
||||
let page: Page;
|
||||
let closeCallback: Function;
|
||||
export function onShownModally(args: ShownModallyData) {
|
||||
page = <Page>args.object;
|
||||
page.bindingContext = new SampleModal();
|
||||
closeCallback = args.closeCallback;
|
||||
|
||||
if (args.context) {
|
||||
args.context.shownModally = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class SampleModal extends Observable {
|
||||
close() {
|
||||
// TODO: a11y
|
||||
// if (global.isIOS) {
|
||||
// (<UIViewController>page.ios).view.accessibilityPerformEscape();
|
||||
// }
|
||||
closeCallback();
|
||||
}
|
||||
}
|
||||
11
apps/toolbox/src/pages/sample-modal.xml
Normal file
11
apps/toolbox/src/pages/sample-modal.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<Page xmlns="http://schemas.nativescript.org/tns.xsd" shownModally="onShownModally" class="page">
|
||||
|
||||
<GridLayout padding="20">
|
||||
<ScrollView>
|
||||
<StackLayout>
|
||||
<Button text="Close" class="view-item" tap="{{close}}" />
|
||||
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</GridLayout>
|
||||
</Page>
|
||||
@ -29,6 +29,7 @@
|
||||
"@nrwl/workspace": "11.4.0",
|
||||
"@nstudio/focus": "~11.1.0",
|
||||
"@nstudio/nps-i": "~1.1.0",
|
||||
"@prettier/plugin-xml": "^0.13.1",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/jest": "~26.0.8",
|
||||
"@types/mocha": "^7.0.2",
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { EventData, EventDataValue } from '../data/observable';
|
||||
import type { View } from '../ui/core/view';
|
||||
import type { Page } from '../ui/page';
|
||||
import type { AccessibilityBlurEventData, AccessibilityFocusChangedEventData, AccessibilityFocusEventData } from './accessibility-types';
|
||||
|
||||
const lastFocusedViewOnPageKeyName = '__lastFocusedViewOnPage';
|
||||
|
||||
export const accessibilityBlurEvent = 'accessibilityBlur';
|
||||
export const accessibilityFocusEvent = 'accessibilityFocus';
|
||||
export const accessibilityFocusChangedEvent = 'accessibilityFocusChanged';
|
||||
export const accessibilityPerformEscapeEvent = 'accessibilityPerformEscape';
|
||||
|
||||
/**
|
||||
* Send notification when accessibility focus state changes.
|
||||
@ -18,7 +19,7 @@ export const accessibilityFocusChangedEvent = 'accessibilityFocusChanged';
|
||||
* @param {boolean} receivedFocus
|
||||
* @param {boolean} lostFocus
|
||||
*/
|
||||
export function notifyAccessibilityFocusState(view: View, receivedFocus: boolean, lostFocus: boolean): void {
|
||||
export function notifyAccessibilityFocusState(view: Partial<View>, receivedFocus: boolean, lostFocus: boolean): void {
|
||||
if (!receivedFocus && !lostFocus) {
|
||||
return;
|
||||
}
|
||||
@ -27,7 +28,7 @@ export function notifyAccessibilityFocusState(view: View, receivedFocus: boolean
|
||||
eventName: accessibilityFocusChangedEvent,
|
||||
object: view,
|
||||
value: !!receivedFocus,
|
||||
} as AccessibilityFocusChangedEventData);
|
||||
} as EventDataValue);
|
||||
|
||||
if (receivedFocus) {
|
||||
if (view.page) {
|
||||
@ -37,12 +38,12 @@ export function notifyAccessibilityFocusState(view: View, receivedFocus: boolean
|
||||
view.notify({
|
||||
eventName: accessibilityFocusEvent,
|
||||
object: view,
|
||||
} as AccessibilityFocusEventData);
|
||||
} as EventData);
|
||||
} else if (lostFocus) {
|
||||
view.notify({
|
||||
eventName: accessibilityBlurEvent,
|
||||
object: view,
|
||||
} as AccessibilityBlurEventData);
|
||||
} as EventData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,16 +24,12 @@ function makePropertyEnumConverter<T>(enumValues) {
|
||||
};
|
||||
}
|
||||
|
||||
export const accessibilityEnabledProperty = new Property<View, boolean>({
|
||||
export const accessibilityEnabledProperty = new CssProperty<Style, boolean>({
|
||||
name: 'accessible',
|
||||
// cssName: 'a11y-enabled',
|
||||
defaultValue: false,
|
||||
valueConverter: (v) => {
|
||||
console.log('accessibilityEnabledProperty:', v);
|
||||
return booleanConverter(v);
|
||||
},
|
||||
cssName: 'a11y-enabled',
|
||||
valueConverter: booleanConverter,
|
||||
});
|
||||
// accessibilityEnabledProperty.register(Style);
|
||||
accessibilityEnabledProperty.register(Style);
|
||||
|
||||
const accessibilityHiddenPropertyName = 'accessibilityHidden';
|
||||
const accessibilityHiddenCssName = 'a11y-hidden';
|
||||
@ -81,6 +77,11 @@ export const accessibilityHintProperty = new Property<View, string>({
|
||||
name: 'accessibilityHint',
|
||||
});
|
||||
|
||||
export const accessibilityIgnoresInvertColorsProperty = new Property<View, boolean>({
|
||||
name: 'accessibilityIgnoresInvertColors',
|
||||
valueConverter: booleanConverter,
|
||||
});
|
||||
|
||||
export const accessibilityLiveRegionProperty = new CssProperty<Style, AccessibilityLiveRegion>({
|
||||
name: 'accessibilityLiveRegion',
|
||||
cssName: 'a11y-live-region',
|
||||
|
||||
@ -30,8 +30,8 @@ class AndroidSharedA11YObservable extends SharedA11YObservable {
|
||||
}
|
||||
}
|
||||
|
||||
let accessibilityStateChangeListener: androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener;
|
||||
let touchExplorationStateChangeListener: androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener;
|
||||
let accessibilityStateChangeListener: android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
|
||||
let touchExplorationStateChangeListener: android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
|
||||
let sharedA11YObservable: AndroidSharedA11YObservable;
|
||||
|
||||
function updateAccessibilityState(): void {
|
||||
@ -72,7 +72,7 @@ function ensureStateListener(): SharedA11YObservable {
|
||||
},
|
||||
});
|
||||
|
||||
touchExplorationStateChangeListener = new androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener({
|
||||
touchExplorationStateChangeListener = new android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener({
|
||||
onTouchExplorationStateChanged(enabled) {
|
||||
updateAccessibilityState();
|
||||
|
||||
@ -83,7 +83,7 @@ function ensureStateListener(): SharedA11YObservable {
|
||||
});
|
||||
|
||||
accessibilityManager.addAccessibilityStateChangeListener(accessibilityStateChangeListener);
|
||||
androidx.core.view.accessibility.AccessibilityManagerCompat.addTouchExplorationStateChangeListener(accessibilityManager, touchExplorationStateChangeListener);
|
||||
accessibilityManager.addTouchExplorationStateChangeListener(touchExplorationStateChangeListener);
|
||||
|
||||
updateAccessibilityState();
|
||||
|
||||
@ -109,7 +109,7 @@ Application.on(Application.exitEvent, (args: Application.ApplicationEventData) =
|
||||
}
|
||||
|
||||
if (touchExplorationStateChangeListener) {
|
||||
androidx.core.view.accessibility.AccessibilityManagerCompat.removeTouchExplorationStateChangeListener(accessibilityManager, touchExplorationStateChangeListener);
|
||||
accessibilityManager.removeTouchExplorationStateChangeListener(touchExplorationStateChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,77 +1,6 @@
|
||||
import type { EventData } from '../data/observable';
|
||||
import type { View } from '../ui/core/view';
|
||||
|
||||
export enum AccessibilityTrait {
|
||||
/**
|
||||
* The element has no traits.
|
||||
*/
|
||||
None = 'none',
|
||||
|
||||
/**
|
||||
* The element should be treated as a button.
|
||||
*/
|
||||
Button = 'button',
|
||||
|
||||
/**
|
||||
* The element should be treated as a link.
|
||||
*/
|
||||
Link = 'link',
|
||||
|
||||
/**
|
||||
* The element should be treated as a search field.
|
||||
*/
|
||||
SearchField = 'search',
|
||||
|
||||
/**
|
||||
* The element should be treated as an image.
|
||||
*/
|
||||
Image = 'image',
|
||||
|
||||
/**
|
||||
* The element is currently selected.
|
||||
*/
|
||||
Selected = 'selected',
|
||||
|
||||
/**
|
||||
* The element plays its own sound when activated.
|
||||
*/
|
||||
PlaysSound = 'plays',
|
||||
|
||||
/**
|
||||
* The element behaves as a keyboard key.
|
||||
*/
|
||||
KeyboardKey = 'key',
|
||||
|
||||
/**
|
||||
* The element should be treated as static text that cannot change.
|
||||
*/
|
||||
StaticText = 'text',
|
||||
|
||||
/**
|
||||
* The element provides summary information when the application starts.
|
||||
*/
|
||||
SummaryElement = 'summary',
|
||||
|
||||
/**
|
||||
* The element is not enabled and does not respond to user interaction.
|
||||
*/
|
||||
NotEnabled = 'disabled',
|
||||
|
||||
/**
|
||||
* The element frequently updates its label or value.
|
||||
*/
|
||||
UpdatesFrequently = 'frequentUpdates',
|
||||
|
||||
/**
|
||||
* The element starts a media session when it is activated.
|
||||
*/
|
||||
StartsMediaSession = 'startsMedia',
|
||||
|
||||
/**
|
||||
* The element allows continuous adjustment through a range of values.
|
||||
*/
|
||||
Adjustable = 'adjustable',
|
||||
|
||||
/**
|
||||
* The element allows direct touch interaction for VoiceOver users.
|
||||
*/
|
||||
@ -84,16 +13,26 @@ export enum AccessibilityTrait {
|
||||
CausesPageTurn = 'pageTurn',
|
||||
|
||||
/**
|
||||
* The element is a header that divides content into sections, such as the title of a navigation bar.
|
||||
* The element is not enabled and does not respond to user interaction.
|
||||
*/
|
||||
Header = 'header',
|
||||
NotEnabled = 'disabled',
|
||||
|
||||
/**
|
||||
* The element is currently selected.
|
||||
*/
|
||||
Selected = 'selected',
|
||||
|
||||
/**
|
||||
* The element frequently updates its label or value.
|
||||
*/
|
||||
UpdatesFrequently = 'frequentUpdates',
|
||||
}
|
||||
|
||||
export enum AccessibilityRole {
|
||||
/**
|
||||
* The element has no traits.
|
||||
* The element allows continuous adjustment through a range of values.
|
||||
*/
|
||||
None = 'none',
|
||||
Adjustable = 'adjustable',
|
||||
|
||||
/**
|
||||
* The element should be treated as a button.
|
||||
@ -101,14 +40,14 @@ export enum AccessibilityRole {
|
||||
Button = 'button',
|
||||
|
||||
/**
|
||||
* The element should be treated as a link.
|
||||
* The element behaves like a Checkbox
|
||||
*/
|
||||
Link = 'link',
|
||||
Checkbox = 'checkbox',
|
||||
|
||||
/**
|
||||
* The element should be treated as a search field.
|
||||
* The element is a header that divides content into sections, such as the title of a navigation bar.
|
||||
*/
|
||||
Search = 'search',
|
||||
Header = 'header',
|
||||
|
||||
/**
|
||||
* The element should be treated as an image.
|
||||
@ -126,29 +65,19 @@ export enum AccessibilityRole {
|
||||
KeyboardKey = 'keyboardKey',
|
||||
|
||||
/**
|
||||
* The element should be treated as static text that cannot change.
|
||||
* The element should be treated as a link.
|
||||
*/
|
||||
StaticText = 'textField',
|
||||
Link = 'link',
|
||||
|
||||
/**
|
||||
* The element allows continuous adjustment through a range of values.
|
||||
* The element has no traits.
|
||||
*/
|
||||
Adjustable = 'adjustable',
|
||||
None = 'none',
|
||||
|
||||
/**
|
||||
* The element provides summary information when the application starts.
|
||||
* The element plays its own sound when activated.
|
||||
*/
|
||||
Summary = 'summary',
|
||||
|
||||
/**
|
||||
* The element is a header that divides content into sections, such as the title of a navigation bar.
|
||||
*/
|
||||
Header = 'header',
|
||||
|
||||
/**
|
||||
* The element behaves like a Checkbox
|
||||
*/
|
||||
Checkbox = 'checkbox',
|
||||
PlaysSound = 'plays',
|
||||
|
||||
/**
|
||||
* The element behaves like a ProgressBar
|
||||
@ -160,11 +89,31 @@ export enum AccessibilityRole {
|
||||
*/
|
||||
RadioButton = 'radioButton',
|
||||
|
||||
/**
|
||||
* The element should be treated as a search field.
|
||||
*/
|
||||
Search = 'search',
|
||||
|
||||
/**
|
||||
* The element behaves like a SpinButton
|
||||
*/
|
||||
SpinButton = 'spinButton',
|
||||
|
||||
/**
|
||||
* The element starts a media session when it is activated.
|
||||
*/
|
||||
StartsMediaSession = 'startsMedia',
|
||||
|
||||
/**
|
||||
* The element should be treated as static text that cannot change.
|
||||
*/
|
||||
StaticText = 'text',
|
||||
|
||||
/**
|
||||
* The element provides summary information when the application starts.
|
||||
*/
|
||||
Summary = 'summary',
|
||||
|
||||
/**
|
||||
* The element behaves like a switch
|
||||
*/
|
||||
@ -184,16 +133,6 @@ export enum AccessibilityLiveRegion {
|
||||
Assertive = 'assertive',
|
||||
}
|
||||
|
||||
export interface AccessibilityFocusEventData extends EventData {
|
||||
object: View;
|
||||
}
|
||||
|
||||
export type AccessibilityBlurEventData = AccessibilityFocusEventData;
|
||||
|
||||
export interface AccessibilityFocusChangedEventData extends AccessibilityFocusEventData {
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
export enum IOSPostAccessibilityNotificationType {
|
||||
Announcement = 'announcement',
|
||||
Screen = 'screen',
|
||||
@ -324,3 +263,13 @@ export enum AndroidAccessibilityEvent {
|
||||
*/
|
||||
ALL_MASK = 'all',
|
||||
}
|
||||
|
||||
export interface AccessibilityEventPerformEscape extends EventData {
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface AccessibilityEventOptions {
|
||||
androidAccessibilityEvent?: AndroidAccessibilityEvent;
|
||||
iosNotificationType?: IOSPostAccessibilityNotificationType;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
@ -12,8 +12,8 @@ export * from './font-scale';
|
||||
|
||||
let clickableRolesMap = new Set<string>();
|
||||
|
||||
let lastFocusedView: WeakRef<View>;
|
||||
function accessibilityEventHelper(view: View, eventType: number) {
|
||||
let lastFocusedView: WeakRef<Partial<View>>;
|
||||
function accessibilityEventHelper(view: Partial<View>, eventType: number) {
|
||||
const eventName = accessibilityEventTypeMap.get(eventType);
|
||||
if (!isAccessibilityServiceEnabled()) {
|
||||
if (Trace.isEnabled()) {
|
||||
@ -102,7 +102,7 @@ function accessibilityEventHelper(view: View, eventType: number) {
|
||||
|
||||
let TNSAccessibilityDelegate: android.view.View.androidviewViewAccessibilityDelegate;
|
||||
|
||||
const androidViewToTNSView = new WeakMap<android.view.View, WeakRef<View>>();
|
||||
const androidViewToTNSView = new WeakMap<android.view.View, WeakRef<Partial<View>>>();
|
||||
|
||||
let accessibilityEventMap: Map<AndroidAccessibilityEvent, number>;
|
||||
let accessibilityEventTypeMap: Map<number, string>;
|
||||
@ -437,11 +437,11 @@ export function isAccessibilityServiceEnabled(): boolean {
|
||||
return accessibilityServiceEnabled;
|
||||
}
|
||||
|
||||
export function setupAccessibleView(view: View): void {
|
||||
export function setupAccessibleView(view: Partial<View>): void {
|
||||
updateAccessibilityProperties(view);
|
||||
}
|
||||
|
||||
export function updateAccessibilityProperties(view: View): void {
|
||||
export function updateAccessibilityProperties(view: Partial<View>): void {
|
||||
if (!view.nativeViewProtected) {
|
||||
return;
|
||||
}
|
||||
@ -537,7 +537,7 @@ export function updateContentDescription(view: View, forceUpdate?: boolean): str
|
||||
return applyContentDescription(view, forceUpdate);
|
||||
}
|
||||
|
||||
function setAccessibilityDelegate(view: View): void {
|
||||
function setAccessibilityDelegate(view: Partial<View>): void {
|
||||
if (!view.nativeViewProtected) {
|
||||
return;
|
||||
}
|
||||
@ -551,7 +551,10 @@ function setAccessibilityDelegate(view: View): void {
|
||||
|
||||
androidViewToTNSView.set(androidView, new WeakRef(view));
|
||||
|
||||
const hasOldDelegate = androidView.getAccessibilityDelegate() === TNSAccessibilityDelegate;
|
||||
let hasOldDelegate = false;
|
||||
if (typeof androidView.getAccessibilityDelegate === 'function') {
|
||||
hasOldDelegate = androidView.getAccessibilityDelegate() === TNSAccessibilityDelegate;
|
||||
}
|
||||
|
||||
if (hasOldDelegate) {
|
||||
return;
|
||||
@ -560,7 +563,7 @@ function setAccessibilityDelegate(view: View): void {
|
||||
androidView.setAccessibilityDelegate(TNSAccessibilityDelegate);
|
||||
}
|
||||
|
||||
function applyContentDescription(view: View, forceUpdate?: boolean) {
|
||||
function applyContentDescription(view: Partial<View>, forceUpdate?: boolean) {
|
||||
let androidView = view.nativeViewProtected as android.view.View;
|
||||
if (!androidView) {
|
||||
return;
|
||||
|
||||
10
packages/core/accessibility/index.d.ts
vendored
10
packages/core/accessibility/index.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
import type { Page } from '../ui/page';
|
||||
import { View } from '../ui/core/view';
|
||||
import { AndroidAccessibilityEvent } from './accessibility-types';
|
||||
import type { View } from '../ui/core/view';
|
||||
import type { AndroidAccessibilityEvent } from './accessibility-types';
|
||||
|
||||
export * from './accessibility-common';
|
||||
export * from './accessibility-types';
|
||||
@ -9,7 +9,7 @@ export * from './font-scale';
|
||||
/**
|
||||
* Initialize accessibility for View. This should be called on loaded-event.
|
||||
*/
|
||||
export function setupAccessibleView(view: View): void;
|
||||
export function setupAccessibleView(view: Partial<View>): void;
|
||||
|
||||
/**
|
||||
* Update accessibility properties on nativeView
|
||||
@ -17,12 +17,12 @@ export function setupAccessibleView(view: View): void;
|
||||
export function updateAccessibilityProperties(view: View): void;
|
||||
|
||||
/**
|
||||
* Android helper function for triggering accessibility events
|
||||
* Android: helper function for triggering accessibility events
|
||||
*/
|
||||
export function sendAccessibilityEvent(View: View, eventName: AndroidAccessibilityEvent, text?: string): void;
|
||||
|
||||
/**
|
||||
* Update the content description for android views
|
||||
* Android: Update the content description for views
|
||||
*/
|
||||
export function updateContentDescription(View: View, forceUpdate?: boolean): string | null;
|
||||
|
||||
|
||||
@ -46,38 +46,30 @@ function ensureNativeClasses() {
|
||||
}
|
||||
|
||||
AccessibilityTraitsMap = new Map<AccessibilityTrait, number>([
|
||||
[AccessibilityTrait.None, UIAccessibilityTraitNone],
|
||||
[AccessibilityTrait.Button, UIAccessibilityTraitButton],
|
||||
[AccessibilityTrait.Link, UIAccessibilityTraitLink],
|
||||
[AccessibilityTrait.SearchField, UIAccessibilityTraitSearchField],
|
||||
[AccessibilityTrait.Image, UIAccessibilityTraitImage],
|
||||
[AccessibilityTrait.Selected, UIAccessibilityTraitSelected],
|
||||
[AccessibilityTrait.PlaysSound, UIAccessibilityTraitPlaysSound],
|
||||
[AccessibilityTrait.StaticText, UIAccessibilityTraitStaticText],
|
||||
[AccessibilityTrait.SummaryElement, UIAccessibilityTraitSummaryElement],
|
||||
[AccessibilityTrait.NotEnabled, UIAccessibilityTraitNotEnabled],
|
||||
[AccessibilityTrait.UpdatesFrequently, UIAccessibilityTraitUpdatesFrequently],
|
||||
[AccessibilityTrait.StartsMediaSession, UIAccessibilityTraitStartsMediaSession],
|
||||
[AccessibilityTrait.Adjustable, UIAccessibilityTraitAdjustable],
|
||||
[AccessibilityTrait.AllowsDirectInteraction, UIAccessibilityTraitAllowsDirectInteraction],
|
||||
[AccessibilityTrait.CausesPageTurn, UIAccessibilityTraitCausesPageTurn],
|
||||
[AccessibilityTrait.Header, UIAccessibilityTraitHeader],
|
||||
[AccessibilityTrait.NotEnabled, UIAccessibilityTraitNotEnabled],
|
||||
[AccessibilityTrait.Selected, UIAccessibilityTraitSelected],
|
||||
[AccessibilityTrait.UpdatesFrequently, UIAccessibilityTraitUpdatesFrequently],
|
||||
]);
|
||||
|
||||
RoleTypeMap = new Map<AccessibilityRole, number>([
|
||||
[AccessibilityRole.Adjustable, UIAccessibilityTraitAdjustable],
|
||||
[AccessibilityRole.Button, UIAccessibilityTraitButton],
|
||||
[AccessibilityRole.Checkbox, UIAccessibilityTraitButton],
|
||||
[AccessibilityRole.Header, UIAccessibilityTraitHeader],
|
||||
[AccessibilityRole.Link, UIAccessibilityTraitLink],
|
||||
[AccessibilityRole.Search, UIAccessibilityTraitSearchField],
|
||||
[AccessibilityRole.KeyboardKey, UIAccessibilityTraitKeyboardKey],
|
||||
[AccessibilityRole.Image, UIAccessibilityTraitImage],
|
||||
[AccessibilityRole.ImageButton, UIAccessibilityTraitImage | UIAccessibilityTraitButton],
|
||||
[AccessibilityRole.KeyboardKey, UIAccessibilityTraitKeyboardKey],
|
||||
[AccessibilityRole.StaticText, UIAccessibilityTraitStaticText],
|
||||
[AccessibilityRole.Summary, UIAccessibilityTraitSummaryElement],
|
||||
[AccessibilityRole.Adjustable, UIAccessibilityTraitAdjustable],
|
||||
[AccessibilityRole.Checkbox, UIAccessibilityTraitButton],
|
||||
[AccessibilityRole.Switch, UIAccessibilityTraitButton],
|
||||
[AccessibilityRole.Link, UIAccessibilityTraitLink],
|
||||
[AccessibilityRole.None, UIAccessibilityTraitNone],
|
||||
[AccessibilityRole.PlaysSound, UIAccessibilityTraitPlaysSound],
|
||||
[AccessibilityRole.RadioButton, UIAccessibilityTraitButton],
|
||||
[AccessibilityRole.Search, UIAccessibilityTraitSearchField],
|
||||
[AccessibilityRole.StaticText, UIAccessibilityTraitStaticText],
|
||||
[AccessibilityRole.StartsMediaSession, UIAccessibilityTraitStartsMediaSession],
|
||||
[AccessibilityRole.Summary, UIAccessibilityTraitSummaryElement],
|
||||
[AccessibilityRole.Switch, UIAccessibilityTraitButton],
|
||||
]);
|
||||
|
||||
nativeFocusedNotificationObserver = Application.ios.addNotificationObserver(UIAccessibilityElementFocusedNotification, (args: NSNotification) => {
|
||||
@ -159,6 +151,11 @@ export function updateAccessibilityProperties(view: View): void {
|
||||
|
||||
return;
|
||||
}
|
||||
console.log('--- Accessible element: ', view.constructor.name);
|
||||
console.log('accessibilityLabel: ', view.accessibilityLabel);
|
||||
console.log('accessibilityRole: ', accessibilityRole);
|
||||
console.log('accessibilityState: ', accessibilityState);
|
||||
console.log('accessibilityValue: ', view.accessibilityValue);
|
||||
|
||||
let a11yTraits = UIAccessibilityTraitNone;
|
||||
if (RoleTypeMap.has(accessibilityRole)) {
|
||||
@ -198,15 +195,23 @@ export function updateAccessibilityProperties(view: View): void {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (view.accessibilityLiveRegion) {
|
||||
console.log('accessibilityLiveRegion:', view.accessibilityLiveRegion);
|
||||
}
|
||||
|
||||
if (view.accessibilityMediaSession) {
|
||||
a11yTraits |= AccessibilityTraitsMap.get(AccessibilityTrait.StartsMediaSession);
|
||||
}
|
||||
|
||||
if (view.accessibilityTraits) {
|
||||
a11yTraits |= inputArrayToBitMask(view.accessibilityTraits, AccessibilityTraitsMap);
|
||||
a11yTraits |= RoleTypeMap.get(AccessibilityRole.StartsMediaSession);
|
||||
}
|
||||
|
||||
// NOTE: There were duplicated types in traits and roles previously which we conslidated
|
||||
// not sure if this is still needed
|
||||
// accessibilityTraits used to be stored on {N} view component but if the above
|
||||
// is combining all traits fresh each time through, don't believe we need to keep track or previous traits
|
||||
// if (view.accessibilityTraits) {
|
||||
// a11yTraits |= inputArrayToBitMask(view.accessibilityTraits, AccessibilityTraitsMap);
|
||||
// }
|
||||
console.log('a11yTraits:', a11yTraits);
|
||||
console.log(' ');
|
||||
uiView.accessibilityTraits = a11yTraits;
|
||||
}
|
||||
|
||||
|
||||
@ -2,12 +2,16 @@ import { Observable as ObservableDefinition, WrappedValue as WrappedValueDefinit
|
||||
|
||||
export interface EventData {
|
||||
eventName: string;
|
||||
object: Observable;
|
||||
object: Partial<Observable>;
|
||||
}
|
||||
|
||||
export interface EventDataValue extends EventData {
|
||||
value?: boolean;
|
||||
}
|
||||
|
||||
export interface NotifyData extends Partial<EventData> {
|
||||
eventName: string;
|
||||
object?: Observable;
|
||||
object?: Partial<Observable>;
|
||||
}
|
||||
|
||||
export interface PropertyChangeData extends EventData {
|
||||
|
||||
6
packages/core/index.d.ts
vendored
6
packages/core/index.d.ts
vendored
@ -56,6 +56,12 @@ export declare const ApplicationSettings: {
|
||||
getNumber: typeof getNumber;
|
||||
setNumber: typeof setNumber;
|
||||
};
|
||||
export declare const AccessibilityEvents: {
|
||||
accessibilityBlurEvent: string;
|
||||
accessibilityFocusEvent: string;
|
||||
accessibilityFocusChangedEvent: string;
|
||||
};
|
||||
export { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait, FontScaleCategory } from './accessibility';
|
||||
export { Color } from './color';
|
||||
import { connectionType, getConnectionType, startMonitoring, stopMonitoring } from './connectivity';
|
||||
export declare const Connectivity: {
|
||||
|
||||
@ -58,6 +58,15 @@ export const ApplicationSettings = {
|
||||
setNumber,
|
||||
};
|
||||
|
||||
import { accessibilityBlurEvent, accessibilityFocusEvent, accessibilityFocusChangedEvent, accessibilityPerformEscapeEvent } from './accessibility';
|
||||
export const AccessibilityEvents = {
|
||||
accessibilityBlurEvent,
|
||||
accessibilityFocusEvent,
|
||||
accessibilityFocusChangedEvent,
|
||||
accessibilityPerformEscapeEvent,
|
||||
};
|
||||
export { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait, FontScaleCategory } from './accessibility';
|
||||
|
||||
export { Color } from './color';
|
||||
|
||||
import { connectionType, getConnectionType, startMonitoring, stopMonitoring } from './connectivity';
|
||||
|
||||
@ -8,7 +8,7 @@ import { AccessibilityRole } from '../../accessibility';
|
||||
export abstract class ButtonBase extends TextBase implements ButtonDefinition {
|
||||
public static tapEvent = 'tap';
|
||||
|
||||
// accessible = true;
|
||||
accessible = true;
|
||||
accessibilityRole = AccessibilityRole.Button;
|
||||
|
||||
get textWrap(): boolean {
|
||||
|
||||
6
packages/core/ui/core/view-base/index.d.ts
vendored
6
packages/core/ui/core/view-base/index.d.ts
vendored
@ -6,7 +6,7 @@ import { Style } from '../../styling/style';
|
||||
import { Page } from '../../page';
|
||||
|
||||
import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from '../../layouts/flexbox-layout';
|
||||
import { Length } from '../../styling/style-properties';
|
||||
import { Length, LengthType } from '../../styling/style-properties';
|
||||
import { DOMNode } from '../../../debugger/dom-node';
|
||||
|
||||
/**
|
||||
@ -105,8 +105,8 @@ export interface ShowModalOptions {
|
||||
|
||||
export abstract class ViewBase extends Observable {
|
||||
// Dynamic properties.
|
||||
left: Length;
|
||||
top: Length;
|
||||
left: LengthType;
|
||||
top: LengthType;
|
||||
effectiveLeft: number;
|
||||
effectiveTop: number;
|
||||
dock: 'left' | 'top' | 'right' | 'bottom';
|
||||
|
||||
@ -815,7 +815,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
this._context = context;
|
||||
|
||||
// This will account for nativeView that is created in createNativeView, recycled
|
||||
// or for backward compatability - set before _setupUI in iOS contructor.
|
||||
// or for backward compatibility - set before _setupUI in iOS constructor.
|
||||
let nativeView = this.nativeViewProtected;
|
||||
|
||||
// if (global.isAndroid) {
|
||||
|
||||
@ -21,7 +21,7 @@ import { AndroidActivityBackPressedEventData, android as androidApp } from '../.
|
||||
import { Device } from '../../../platform';
|
||||
import lazy from '../../../utils/lazy';
|
||||
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties';
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, setupAccessibleView, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription } from '../../../accessibility';
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, setupAccessibleView, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility';
|
||||
import * as Utils from '../../../utils';
|
||||
|
||||
export * from './view-common';
|
||||
@ -782,6 +782,7 @@ export class View extends ViewCommon {
|
||||
}
|
||||
|
||||
[accessibilityRoleProperty.setNative](value: AccessibilityRole): void {
|
||||
this.accessibilityRole = value;
|
||||
updateAccessibilityProperties(this);
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 28) {
|
||||
@ -829,7 +830,8 @@ export class View extends ViewCommon {
|
||||
}
|
||||
}
|
||||
|
||||
[accessibilityStateProperty.setNative](): void {
|
||||
[accessibilityStateProperty.setNative](value: AccessibilityState): void {
|
||||
this.accessibilityState = value;
|
||||
updateAccessibilityProperties(this);
|
||||
}
|
||||
|
||||
@ -1102,18 +1104,10 @@ export class View extends ViewCommon {
|
||||
}
|
||||
|
||||
public accessibilityAnnouncement(msg = this.accessibilityLabel): void {
|
||||
if (!isAccessibilityServiceEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.androidSendAccessibilityEvent(AndroidAccessibilityEvent.ANNOUNCEMENT, msg);
|
||||
}
|
||||
|
||||
public accessibilityScreenChanged(): void {
|
||||
if (!isAccessibilityServiceEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.androidSendAccessibilityEvent(AndroidAccessibilityEvent.WINDOW_STATE_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
24
packages/core/ui/core/view/index.d.ts
vendored
24
packages/core/ui/core/view/index.d.ts
vendored
@ -6,7 +6,7 @@ import { Animation, AnimationDefinition, AnimationPromise } from '../../animatio
|
||||
import { LengthType, PercentLengthType } from '../../styling/style-properties';
|
||||
import { GestureTypes, GesturesObserver } from '../../gestures';
|
||||
import { LinearGradient } from '../../styling/gradient';
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait, AndroidAccessibilityEvent, IOSPostAccessibilityNotificationType } from '../../../accessibility/accessibility-types';
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait, AccessibilityEventOptions } from '../../../accessibility/accessibility-types';
|
||||
import { Enums } from '../../enums';
|
||||
import { CSSShadow } from '../../styling/css-shadow';
|
||||
|
||||
@ -286,7 +286,6 @@ export abstract class View extends ViewBase {
|
||||
* A hint describes the elements behavior. Example: 'Tap change playback speed'
|
||||
*/
|
||||
accessibilityHint: string;
|
||||
accessibilityTraits?: AccessibilityTrait[];
|
||||
accessibilityLiveRegion: AccessibilityLiveRegion;
|
||||
|
||||
/**
|
||||
@ -750,17 +749,18 @@ export abstract class View extends ViewBase {
|
||||
public eachChildView(callback: (view: View) => boolean): void;
|
||||
|
||||
/**
|
||||
* Android: Send accessibility event
|
||||
* Send accessibility event
|
||||
* @params options AccessibilityEventOptions
|
||||
* androidAccessibilityEvent: AndroidAccessibilityEvent;
|
||||
* iosNotificationType: IOSPostAccessibilityNotificationType;
|
||||
* message: string;
|
||||
*
|
||||
* iOS Notes:
|
||||
* type = 'announcement' will announce `args` via VoiceOver. If no args element will be announced instead.
|
||||
* type = 'layout' used when the layout of a screen changes.
|
||||
* type = 'screen' large change made to the screen.
|
||||
*/
|
||||
public androidSendAccessibilityEvent(eventName: AndroidAccessibilityEvent, msg?: string): void;
|
||||
|
||||
/**
|
||||
* iOS: post accessibility notification.
|
||||
* type = 'announcement' will announce `args` via VoiceOver. If no args element will be announced instead.
|
||||
* type = 'layout' used when the layout of a screen changes.
|
||||
* type = 'screen' large change made to the screen.
|
||||
*/
|
||||
public iosPostAccessibilityNotification(notificationType: IOSPostAccessibilityNotificationType, msg?: string): void;
|
||||
public sendAccessibilityEvent(options: Partial<AccessibilityEventOptions>): void;
|
||||
|
||||
/**
|
||||
* Make an announcement to the screen reader.
|
||||
|
||||
@ -10,8 +10,8 @@ import { IOSHelper } from './view-helper';
|
||||
import { ios as iosBackground, Background } from '../../styling/background';
|
||||
import { perspectiveProperty, visibilityProperty, opacityProperty, rotateProperty, rotateXProperty, rotateYProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty, zIndexProperty, backgroundInternalProperty, clipPathProperty } from '../../styling/style-properties';
|
||||
import { profile } from '../../../profiling';
|
||||
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityTraitsProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties';
|
||||
import { setupAccessibleView, IOSPostAccessibilityNotificationType, isAccessibilityServiceEnabled, updateAccessibilityProperties } from '../../../accessibility';
|
||||
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty, accessibilityIgnoresInvertColorsProperty } from '../../../accessibility/accessibility-properties';
|
||||
import { setupAccessibleView, IOSPostAccessibilityNotificationType, isAccessibilityServiceEnabled, updateAccessibilityProperties, AccessibilityEventOptions, AccessibilityRole, AccessibilityState } from '../../../accessibility';
|
||||
import { Enums } from '../../enums';
|
||||
|
||||
export * from './view-common';
|
||||
@ -504,6 +504,13 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
}
|
||||
this._modalAnimatedOptions.push(animated);
|
||||
|
||||
// TODO: a11y
|
||||
// controller.accessibilityViewIsModal = true;
|
||||
// controller.accessibilityPerformEscape = () => {
|
||||
// console.log('accessibilityPerformEscape!!')
|
||||
// return true;
|
||||
// }
|
||||
|
||||
parentController.presentViewControllerAnimatedCompletion(controller, animated, null);
|
||||
const transitionCoordinator = parentController.transitionCoordinator;
|
||||
if (transitionCoordinator) {
|
||||
@ -575,11 +582,8 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
this.nativeViewProtected.accessibilityIdentifier = value;
|
||||
}
|
||||
|
||||
[accessibilityRoleProperty.setNative](): void {
|
||||
updateAccessibilityProperties(this);
|
||||
}
|
||||
|
||||
[accessibilityTraitsProperty.setNative](): void {
|
||||
[accessibilityRoleProperty.setNative](value: AccessibilityRole): void {
|
||||
this.accessibilityRole = value;
|
||||
updateAccessibilityProperties(this);
|
||||
}
|
||||
|
||||
@ -590,7 +594,12 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
|
||||
[accessibilityLabelProperty.setNative](value: string): void {
|
||||
value = value == null ? null : `${value}`;
|
||||
// not sure if needed for Label:
|
||||
// if ((<any>this).nativeTextViewProtected) {
|
||||
// (<any>this).nativeTextViewProtected.accessibilityLabel = value;
|
||||
// } else {
|
||||
this.nativeViewProtected.accessibilityLabel = value;
|
||||
// }
|
||||
}
|
||||
|
||||
[accessibilityHintProperty.setNative](value: string): void {
|
||||
@ -598,6 +607,11 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
this.nativeViewProtected.accessibilityHint = value;
|
||||
}
|
||||
|
||||
[accessibilityIgnoresInvertColorsProperty.setNative](value: boolean) {
|
||||
console.log('accessibilityIgnoresInvertColorsProperty:', !!value);
|
||||
this.nativeViewProtected.accessibilityIgnoresInvertColors = !!value;
|
||||
}
|
||||
|
||||
[accessibilityLanguageProperty.setNative](value: string): void {
|
||||
value = value == null ? null : `${value}`;
|
||||
this.nativeViewProtected.accessibilityLanguage = value;
|
||||
@ -613,7 +627,8 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
updateAccessibilityProperties(this);
|
||||
}
|
||||
|
||||
[accessibilityStateProperty.setNative](): void {
|
||||
[accessibilityStateProperty.setNative](value: AccessibilityState): void {
|
||||
this.accessibilityState = value;
|
||||
updateAccessibilityProperties(this);
|
||||
}
|
||||
|
||||
@ -733,8 +748,12 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
public iosPostAccessibilityNotification(notificationType: IOSPostAccessibilityNotificationType, msg?: string): void {
|
||||
if (!notificationType) {
|
||||
public sendAccessibilityEvent(options: Partial<AccessibilityEventOptions>): void {
|
||||
if (!isAccessibilityServiceEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.iosNotificationType) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -744,7 +763,7 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
args = msg;
|
||||
}
|
||||
|
||||
switch (notificationType) {
|
||||
switch (options.iosNotificationType) {
|
||||
case IOSPostAccessibilityNotificationType.Announcement: {
|
||||
notification = UIAccessibilityAnnouncementNotification;
|
||||
break;
|
||||
@ -766,19 +785,16 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
}
|
||||
|
||||
public accessibilityAnnouncement(msg = this.accessibilityLabel): void {
|
||||
if (!isAccessibilityServiceEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.iosPostAccessibilityNotification(IOSPostAccessibilityNotificationType.Announcement, msg);
|
||||
this.sendAccessibilityEvent({
|
||||
iosNotificationType: IOSPostAccessibilityNotificationType.Announcement,
|
||||
message: msg,
|
||||
});
|
||||
}
|
||||
|
||||
public accessibilityScreenChanged(): void {
|
||||
if (!isAccessibilityServiceEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.iosPostAccessibilityNotification(IOSPostAccessibilityNotificationType.Screen);
|
||||
this.sendAccessibilityEvent({
|
||||
iosNotificationType: IOSPostAccessibilityNotificationType.Screen,
|
||||
});
|
||||
}
|
||||
|
||||
_getCurrentLayoutBounds(): {
|
||||
|
||||
@ -22,9 +22,9 @@ import { StyleScope } from '../../styling/style-scope';
|
||||
import { LinearGradient } from '../../styling/linear-gradient';
|
||||
|
||||
import * as am from '../../animation';
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait, AndroidAccessibilityEvent, IOSPostAccessibilityNotificationType } from '../../../accessibility/accessibility-types';
|
||||
import { accessibilityEnabledProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityTraitsProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties';
|
||||
import { accessibilityBlurEvent, accessibilityFocusChangedEvent, accessibilityFocusEvent, getCurrentFontScale } from '../../../accessibility';
|
||||
import { AccessibilityEventOptions, AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait } from '../../../accessibility/accessibility-types';
|
||||
import { accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityValueProperty, accessibilityIgnoresInvertColorsProperty } from '../../../accessibility/accessibility-properties';
|
||||
import { accessibilityBlurEvent, accessibilityFocusChangedEvent, accessibilityFocusEvent, accessibilityPerformEscapeEvent, getCurrentFontScale } from '../../../accessibility';
|
||||
import { CSSShadow } from '../../styling/css-shadow';
|
||||
|
||||
// helpers (these are okay re-exported here)
|
||||
@ -74,6 +74,12 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
public static accessibilityBlurEvent = accessibilityBlurEvent;
|
||||
public static accessibilityFocusEvent = accessibilityFocusEvent;
|
||||
public static accessibilityFocusChangedEvent = accessibilityFocusChangedEvent;
|
||||
public static accessibilityPerformEscapeEvent = accessibilityPerformEscapeEvent;
|
||||
|
||||
public accessibilityIdentifier: string;
|
||||
public accessibilityLabel: string;
|
||||
public accessibilityValue: string;
|
||||
public accessibilityHint: string;
|
||||
|
||||
protected _closeModalCallback: Function;
|
||||
public _manager: any;
|
||||
@ -99,9 +105,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
|
||||
_androidContentDescriptionUpdated?: boolean;
|
||||
|
||||
// a11y
|
||||
_accessible: boolean;
|
||||
|
||||
get css(): string {
|
||||
const scope = this._styleScope;
|
||||
|
||||
@ -757,12 +760,12 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
}
|
||||
|
||||
get accessible(): boolean {
|
||||
// return this.style.accessible;
|
||||
return this._accessible;
|
||||
return this.style.accessible;
|
||||
// return this._accessible;
|
||||
}
|
||||
set accessible(value: boolean) {
|
||||
// this.style.accessible = value;
|
||||
this._accessible = value;
|
||||
this.style.accessible = value;
|
||||
// this._accessible = value;
|
||||
}
|
||||
|
||||
get accessibilityHidden(): boolean {
|
||||
@ -772,8 +775,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
this.style.accessibilityHidden = value;
|
||||
}
|
||||
|
||||
public accessibilityIdentifier: string;
|
||||
|
||||
get accessibilityRole(): AccessibilityRole {
|
||||
return this.style.accessibilityRole;
|
||||
}
|
||||
@ -788,10 +789,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
this.style.accessibilityState = value;
|
||||
}
|
||||
|
||||
public accessibilityLabel: string;
|
||||
public accessibilityValue: string;
|
||||
public accessibilityHint: string;
|
||||
|
||||
get accessibilityLiveRegion(): AccessibilityLiveRegion {
|
||||
return this.style.accessibilityLiveRegion;
|
||||
}
|
||||
@ -813,8 +810,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
this.style.accessibilityMediaSession = value;
|
||||
}
|
||||
|
||||
public accessibilityTraits?: AccessibilityTrait[];
|
||||
|
||||
get automationText(): string {
|
||||
return this.accessibilityIdentifier;
|
||||
}
|
||||
@ -1093,11 +1088,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
return false;
|
||||
}
|
||||
|
||||
public androidSendAccessibilityEvent(eventName: AndroidAccessibilityEvent, msg?: string): void {
|
||||
return;
|
||||
}
|
||||
|
||||
public iosPostAccessibilityNotification(notificationType: IOSPostAccessibilityNotificationType, msg?: string): void {
|
||||
public sendAccessibilityEvent(options: Partial<AccessibilityEventOptions>): void {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1160,9 +1151,8 @@ export const iosIgnoreSafeAreaProperty = new InheritedProperty({
|
||||
valueConverter: booleanConverter,
|
||||
});
|
||||
iosIgnoreSafeAreaProperty.register(ViewCommon);
|
||||
accessibilityEnabledProperty.register(ViewCommon);
|
||||
accessibilityIdentifierProperty.register(ViewCommon);
|
||||
accessibilityLabelProperty.register(ViewCommon);
|
||||
accessibilityValueProperty.register(ViewCommon);
|
||||
accessibilityHintProperty.register(ViewCommon);
|
||||
accessibilityTraitsProperty.register(ViewCommon);
|
||||
accessibilityIgnoresInvertColorsProperty.register(ViewCommon);
|
||||
|
||||
@ -46,21 +46,21 @@ export namespace IOSHelper {
|
||||
* Returns a view with viewController or undefined if no such found along the view's parent chain.
|
||||
* @param view The view form which to start the search.
|
||||
*/
|
||||
export function getParentWithViewController(view: View): View;
|
||||
export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: View): void;
|
||||
export function updateConstraints(controller: any /* UIViewController */, owner: View): void;
|
||||
export function layoutView(controller: any /* UIViewController */, owner: View): void;
|
||||
export function getParentWithViewController(view: Partial<View>): View;
|
||||
export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: Partial<View>): void;
|
||||
export function updateConstraints(controller: any /* UIViewController */, owner: Partial<View>): void;
|
||||
export function layoutView(controller: any /* UIViewController */, owner: Partial<View>): void;
|
||||
export function getPositionFromFrame(frame: any /* CGRect */): { left; top; right; bottom };
|
||||
export function getFrameFromPosition(position: { left; top; right; bottom }, insets?: { left; top; right; bottom }): any; /* CGRect */
|
||||
export function shrinkToSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */
|
||||
export function expandBeyondSafeArea(view: View, frame: any /* CGRect */): any; /* CGRect */
|
||||
export function shrinkToSafeArea(view: Partial<View>, frame: any /* CGRect */): any; /* CGRect */
|
||||
export function expandBeyondSafeArea(view: Partial<View>, frame: any /* CGRect */): any; /* CGRect */
|
||||
export class UILayoutViewController {
|
||||
public static initWithOwner(owner: WeakRef<View>): UILayoutViewController;
|
||||
public static initWithOwner(owner: WeakRef<Partial<View>>): UILayoutViewController;
|
||||
}
|
||||
export class UIAdaptivePresentationControllerDelegateImp {
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIAdaptivePresentationControllerDelegateImp;
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<Partial<View>>, whenClosedCallback: Function): UIAdaptivePresentationControllerDelegateImp;
|
||||
}
|
||||
export class UIPopoverPresentationControllerDelegateImp {
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIPopoverPresentationControllerDelegateImp;
|
||||
public static initWithOwnerAndCallback(owner: WeakRef<Partial<View>>, whenClosedCallback: Function): UIPopoverPresentationControllerDelegateImp;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Observable, EventData } from '../../../data/observable';
|
||||
|
||||
const handlersForEventName = new Map<string, (eventData: EventData) => void>();
|
||||
const sourcesMap = new WeakMap<Observable, Map<string, Array<TargetHandlerPair>>>();
|
||||
const sourcesMap = new WeakMap<Partial<Observable>, Map<string, Array<TargetHandlerPair>>>();
|
||||
|
||||
class TargetHandlerPair {
|
||||
tagetRef: WeakRef<Object>;
|
||||
|
||||
4
packages/core/ui/gestures/index.d.ts
vendored
4
packages/core/ui/gestures/index.d.ts
vendored
@ -119,7 +119,7 @@ export interface GestureEventData extends EventData {
|
||||
/**
|
||||
* Gets the view which originates the gesture.
|
||||
*/
|
||||
view: View;
|
||||
view: Partial<View>;
|
||||
/**
|
||||
* Gets the underlying native iOS specific [UIGestureRecognizer](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/).
|
||||
*/
|
||||
@ -266,7 +266,7 @@ export class GesturesObserver {
|
||||
* @param callback - A function that will be executed when a gesture is received.
|
||||
* @param context - default this argument for the callbacks.
|
||||
*/
|
||||
constructor(target: View, callback: (args: GestureEventData) => void, context: any);
|
||||
constructor(target: Partial<View>, callback: (args: GestureEventData) => void, context: any);
|
||||
|
||||
/**
|
||||
* Registers a gesture observer to a view and gesture.
|
||||
|
||||
6
packages/core/ui/page/index.d.ts
vendored
6
packages/core/ui/page/index.d.ts
vendored
@ -99,6 +99,12 @@ export declare class Page extends PageBase {
|
||||
// @ts-ignore
|
||||
public actionBar: ActionBar;
|
||||
|
||||
/**
|
||||
* iOS Only
|
||||
* Perform an action when user performans the "escape" gesture
|
||||
*/
|
||||
public onAccessibilityPerformEscape?: () => boolean;
|
||||
|
||||
/**
|
||||
* Should page changed be annnounced to the screen reader.
|
||||
*/
|
||||
|
||||
@ -66,6 +66,10 @@ function isBackNavigationFrom(controller: UIViewControllerImpl, page: Page): boo
|
||||
|
||||
@NativeClass
|
||||
class UIViewControllerImpl extends UIViewController {
|
||||
// TODO: a11y
|
||||
// static ObjCExposedMethods = {
|
||||
// accessibilityPerformEscape: { returns: interop.types.bool, params: [interop.types.void] },
|
||||
// };
|
||||
private _owner: WeakRef<Page>;
|
||||
|
||||
public isBackstackSkipped: boolean;
|
||||
@ -311,6 +315,21 @@ class UIViewControllerImpl extends UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: a11y
|
||||
// public accessibilityPerformEscape() {
|
||||
// const owner = this._owner.get();
|
||||
// if (!owner) {
|
||||
// return false;
|
||||
// }
|
||||
// console.log('page accessibilityPerformEscape');
|
||||
// if (owner.onAccessibilityPerformEscape) {
|
||||
// const result = owner.onAccessibilityPerformEscape();
|
||||
// return result;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// @ts-ignore
|
||||
public get preferredStatusBarStyle(): UIStatusBarStyle {
|
||||
const owner = this._owner.get();
|
||||
@ -325,6 +344,7 @@ class UIViewControllerImpl extends UIViewController {
|
||||
export class Page extends PageBase {
|
||||
nativeViewProtected: UIView;
|
||||
viewController: UIViewControllerImpl;
|
||||
onAccessibilityPerformEscape: () => boolean;
|
||||
|
||||
private _backgroundColor = majorVersion <= 12 && !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
|
||||
private _ios: UIViewControllerImpl;
|
||||
|
||||
@ -21,7 +21,7 @@ export class SliderBase extends View implements SliderDefinition {
|
||||
this.style.accessibilityStep = value;
|
||||
}
|
||||
|
||||
// accessible = true;
|
||||
accessible = true;
|
||||
accessibilityRole = AccessibilityRole.Adjustable;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user