@24i/bigscreen-sdk 1.0.6-alpha.2139 → 1.0.6-alpha.2147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/developer-tools/src/TechnicalInfo/TechnicalInfoProvider.ts +9 -0
- package/packages/developer-tools/src/TechnicalInfo/translations.ts +2 -0
- package/packages/focus/README.md +10 -0
- package/packages/focus/src/Layout/Base.tsx +31 -6
- package/packages/focus/src/Layout/Matrix.tsx +33 -1
- package/packages/focus/src/Layout/__test__/Base.spec.ts +31 -20
- package/packages/focus/src/Layout/__test__/Horizontal.spec.ts +0 -2
- package/packages/focus/src/Layout/__test__/Matrix.spec.ts +56 -18
- package/packages/focus/src/Layout/__test__/Vertical.spec.ts +0 -2
- package/packages/l10n/src/date/he.ts +101 -0
- package/packages/l10n/src/l10n.ts +2 -0
package/package.json
CHANGED
|
@@ -35,6 +35,8 @@ type Props = {
|
|
|
35
35
|
profileName: string,
|
|
36
36
|
/** Version of application. */
|
|
37
37
|
appVersion: string,
|
|
38
|
+
/** Version of the currently used core packages. */
|
|
39
|
+
appCoreVersions: string,
|
|
38
40
|
/** App resolution, in format like "1280x720". */
|
|
39
41
|
appResolution: string,
|
|
40
42
|
/** OVP/CMS back-end base URL. */
|
|
@@ -263,6 +265,11 @@ class TechnicalInfoProvider implements ITechnicalInfoProvider {
|
|
|
263
265
|
return this.props.backendBaseUrl || unknown;
|
|
264
266
|
}
|
|
265
267
|
|
|
268
|
+
async getAppCoreVersions() {
|
|
269
|
+
const { unknown } = this.translations;
|
|
270
|
+
return this.props.appCoreVersions || unknown;
|
|
271
|
+
}
|
|
272
|
+
|
|
266
273
|
getSourceDataLayout() {
|
|
267
274
|
const {
|
|
268
275
|
username, profile, appVersion, firmwareVersion, deviceModel,
|
|
@@ -270,11 +277,13 @@ class TechnicalInfoProvider implements ITechnicalInfoProvider {
|
|
|
270
277
|
publicIpAddress, connectionType, macAddress, memoryHeapSize,
|
|
271
278
|
usedLocalStorage, timeZone, dateAndTime, flavour, userAgent,
|
|
272
279
|
emeSupport, emeDrmSystems, mseSupport, fourKSupport, resolution, baseUrl,
|
|
280
|
+
appCoreVersions,
|
|
273
281
|
} = this.translations;
|
|
274
282
|
const dataLayout: SourceDataLayout = [
|
|
275
283
|
{ label: username, getter: this.getUserName },
|
|
276
284
|
{ label: profile, getter: this.getProfileName },
|
|
277
285
|
{ label: appVersion, getter: this.getAppVersion },
|
|
286
|
+
{ label: appCoreVersions, getter: this.getAppCoreVersions },
|
|
278
287
|
{ label: firmwareVersion, getter: this.getFirmwareVersion },
|
|
279
288
|
{ label: deviceModel, getter: this.getDeviceModelName },
|
|
280
289
|
{ label: deviceManufacturer, getter: this.getDeviceManufacturer },
|
|
@@ -10,6 +10,7 @@ export type TranslationKeys = {
|
|
|
10
10
|
username: string,
|
|
11
11
|
profile: string,
|
|
12
12
|
appVersion: string,
|
|
13
|
+
appCoreVersions: string,
|
|
13
14
|
firmwareVersion: string,
|
|
14
15
|
deviceModel: string,
|
|
15
16
|
deviceManufacturer: string,
|
|
@@ -47,6 +48,7 @@ export const defaultTranslations = {
|
|
|
47
48
|
username: 'Username',
|
|
48
49
|
profile: 'Profile',
|
|
49
50
|
appVersion: 'App version',
|
|
51
|
+
appCoreVersions: 'Core versions',
|
|
50
52
|
firmwareVersion: 'Firmware version',
|
|
51
53
|
deviceModel: 'Device model',
|
|
52
54
|
deviceManufacturer: 'Device manufacturer',
|
package/packages/focus/README.md
CHANGED
|
@@ -128,6 +128,16 @@ const matrix = [
|
|
|
128
128
|
];
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
+
## Prevent blur on long press
|
|
132
|
+
When the user holds a navigation key, he usually scrolls to the boundary of the layout
|
|
133
|
+
and then releases the key. This operation is very quick and most of the time he releases
|
|
134
|
+
the key too late, so the keydown event from the layout will propagate to another component
|
|
135
|
+
or another layout. In some cases, we don't want this behavior and for those situations and
|
|
136
|
+
to have better control above the layout navigation, there is a prop `preventBlurOnLongPress`.
|
|
137
|
+
When the user scrolls to the boundary of the layout while holding a navigation key,
|
|
138
|
+
he needs to release the key and press it again to propagate the event higher and focus
|
|
139
|
+
another component or layout.
|
|
140
|
+
|
|
131
141
|
## Limitations
|
|
132
142
|
For the div to properly receive keydown events it needs to have set `tabIndex`. If you do not set `tabIndex` then it won't receive keydown events and those will not propagate to the Vertical/Horizontal component.
|
|
133
143
|
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { Component, DeclareProps, Reference } from '@24i/bigscreen-sdk/jsx';
|
|
2
2
|
import { noop } from '@24i/bigscreen-sdk/utils/noop';
|
|
3
|
+
import { stopEvent } from '@24i/bigscreen-sdk/utils/stopEvent';
|
|
3
4
|
import { IFocusable } from '../IFocusable';
|
|
4
5
|
import { Errors, hasValidDomRef } from './constants';
|
|
5
6
|
import { SharedProps } from './types';
|
|
6
7
|
|
|
8
|
+
const PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS = 2;
|
|
9
|
+
|
|
7
10
|
export type Props = SharedProps & {
|
|
8
11
|
focusOptions?: FocusOptions;
|
|
9
12
|
focusableChildren: Array<Reference>,
|
|
10
13
|
onFocusChanged?: (focused: Reference, index: number) => any,
|
|
11
14
|
onBeforeFocusChange?: (focused: Reference, index: number) => any,
|
|
15
|
+
preventBlurOnLongPress?: {
|
|
16
|
+
forward?: boolean,
|
|
17
|
+
backward?: boolean,
|
|
18
|
+
}
|
|
12
19
|
};
|
|
13
20
|
|
|
14
21
|
export interface ILayout {
|
|
@@ -25,12 +32,15 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
25
32
|
class Layout extends Component<Props> implements ILayout, IFocusable {
|
|
26
33
|
index = 0;
|
|
27
34
|
|
|
35
|
+
preventBlurCounter = 0;
|
|
36
|
+
|
|
28
37
|
static defaultProps = {
|
|
29
38
|
onFocusChanged: noop,
|
|
30
39
|
onBeforeFocusChange: noop,
|
|
31
40
|
focusOptions: { preventScroll: true },
|
|
32
41
|
dir: 'auto',
|
|
33
|
-
|
|
42
|
+
preventBlurOnLongPress: {},
|
|
43
|
+
} as const;
|
|
34
44
|
|
|
35
45
|
declare props: DeclareProps<Props, typeof Layout.defaultProps>;
|
|
36
46
|
|
|
@@ -40,6 +50,7 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
40
50
|
throw new Error(Errors.DOM_REF_ERROR);
|
|
41
51
|
}
|
|
42
52
|
domRef.current!.addEventListener('keydown', this.onKeyDown, false);
|
|
53
|
+
domRef.current!.addEventListener('keyup', this.onKeyUp, false);
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
componentWillUnmount() {
|
|
@@ -48,12 +59,17 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
48
59
|
throw new Error(Errors.DOM_REF_ERROR);
|
|
49
60
|
}
|
|
50
61
|
domRef.current!.removeEventListener('keydown', this.onKeyDown, false);
|
|
62
|
+
domRef.current!.removeEventListener('keyup', this.onKeyUp, false);
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
onKeyDown = (e: KeyboardEvent) => {
|
|
54
66
|
onKeyDown(e, this);
|
|
55
67
|
};
|
|
56
68
|
|
|
69
|
+
onKeyUp = () => {
|
|
70
|
+
this.preventBlurCounter = 0;
|
|
71
|
+
};
|
|
72
|
+
|
|
57
73
|
getFromIndex(target: HTMLElement) {
|
|
58
74
|
const { focusableChildren } = this.props;
|
|
59
75
|
if (
|
|
@@ -78,25 +94,34 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
78
94
|
}
|
|
79
95
|
|
|
80
96
|
backward(e: KeyboardEvent) {
|
|
97
|
+
const { preventBlurOnLongPress } = this.props;
|
|
81
98
|
const { target } = e;
|
|
82
99
|
const fromIndex = this.getFromIndex(target as HTMLElement);
|
|
100
|
+
if (preventBlurOnLongPress.backward) {
|
|
101
|
+
this.preventBlurCounter += 1;
|
|
102
|
+
}
|
|
83
103
|
if (fromIndex > 0) {
|
|
84
104
|
this.setIndex(fromIndex - 1);
|
|
85
105
|
this.focusCurrentIndex();
|
|
86
|
-
e
|
|
87
|
-
|
|
106
|
+
stopEvent(e);
|
|
107
|
+
} else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
|
|
108
|
+
stopEvent(e);
|
|
88
109
|
}
|
|
89
110
|
}
|
|
90
111
|
|
|
91
112
|
forward(e: KeyboardEvent) {
|
|
92
|
-
const { focusableChildren } = this.props;
|
|
113
|
+
const { focusableChildren, preventBlurOnLongPress } = this.props;
|
|
93
114
|
const { target } = e;
|
|
94
115
|
const fromIndex = this.getFromIndex(target as HTMLElement);
|
|
116
|
+
if (preventBlurOnLongPress.forward) {
|
|
117
|
+
this.preventBlurCounter += 1;
|
|
118
|
+
}
|
|
95
119
|
if (fromIndex < focusableChildren.length - 1) {
|
|
96
120
|
this.setIndex(fromIndex + 1);
|
|
97
121
|
this.focusCurrentIndex();
|
|
98
|
-
e
|
|
99
|
-
|
|
122
|
+
stopEvent(e);
|
|
123
|
+
} else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
|
|
124
|
+
stopEvent(e);
|
|
100
125
|
}
|
|
101
126
|
}
|
|
102
127
|
|
|
@@ -8,6 +8,8 @@ import { isRtl } from './isRtl';
|
|
|
8
8
|
import { Errors, hasValidDomRef } from './constants';
|
|
9
9
|
import { SharedProps } from './types';
|
|
10
10
|
|
|
11
|
+
const PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS = 2;
|
|
12
|
+
|
|
11
13
|
// Used in binary operations for shorter checks
|
|
12
14
|
export enum CircularDirection {
|
|
13
15
|
NONE = 0,
|
|
@@ -21,7 +23,13 @@ export type Props = SharedProps & {
|
|
|
21
23
|
matrix: Array<Array<Reference>>,
|
|
22
24
|
onFocusChanged?: (focused: Reference, index: Index2D) => any,
|
|
23
25
|
onBeforeFocusChange?: (focused: Reference, index: Index2D) => any,
|
|
24
|
-
circular?: CircularDirection
|
|
26
|
+
circular?: CircularDirection,
|
|
27
|
+
preventBlurOnLongPress?: {
|
|
28
|
+
left?: boolean,
|
|
29
|
+
right?: boolean,
|
|
30
|
+
up?: boolean,
|
|
31
|
+
down?: boolean,
|
|
32
|
+
},
|
|
25
33
|
};
|
|
26
34
|
|
|
27
35
|
export type Index2D = {
|
|
@@ -32,11 +40,14 @@ export type Index2D = {
|
|
|
32
40
|
export class Matrix extends Component<Props> implements IFocusable {
|
|
33
41
|
index: Index2D = { x: 0, y: 0 };
|
|
34
42
|
|
|
43
|
+
preventBlurCounter = 0;
|
|
44
|
+
|
|
35
45
|
static defaultProps = {
|
|
36
46
|
onFocusChanged: noop,
|
|
37
47
|
onBeforeFocusChange: noop,
|
|
38
48
|
circular: CircularDirection.NONE,
|
|
39
49
|
dir: 'auto',
|
|
50
|
+
preventBlurOnLongPress: {},
|
|
40
51
|
} as const;
|
|
41
52
|
|
|
42
53
|
declare props: DeclareProps<Props, typeof Matrix.defaultProps>;
|
|
@@ -47,6 +58,7 @@ export class Matrix extends Component<Props> implements IFocusable {
|
|
|
47
58
|
throw new Error(Errors.DOM_REF_ERROR);
|
|
48
59
|
}
|
|
49
60
|
domRef.current!.addEventListener('keydown', this.onKeyDown, false);
|
|
61
|
+
domRef.current!.addEventListener('keyup', this.onKeyUp, false);
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
componentWillUnmount() {
|
|
@@ -55,26 +67,46 @@ export class Matrix extends Component<Props> implements IFocusable {
|
|
|
55
67
|
throw new Error(Errors.DOM_REF_ERROR);
|
|
56
68
|
}
|
|
57
69
|
domRef.current!.removeEventListener('keydown', this.onKeyDown, false);
|
|
70
|
+
domRef.current!.removeEventListener('keyup', this.onKeyUp, false);
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
onKeyDown = (e: KeyboardEvent) => {
|
|
74
|
+
const { preventBlurOnLongPress } = this.props;
|
|
61
75
|
let newIndex: Index2D | null = null;
|
|
62
76
|
if (this.isDirectionLeft(e)) {
|
|
63
77
|
newIndex = this.moveHorizontaly('left');
|
|
78
|
+
if (preventBlurOnLongPress.left) {
|
|
79
|
+
this.preventBlurCounter += 1;
|
|
80
|
+
}
|
|
64
81
|
} else if (this.isDirectionRight(e)) {
|
|
65
82
|
newIndex = this.moveHorizontaly('right');
|
|
83
|
+
if (preventBlurOnLongPress.right) {
|
|
84
|
+
this.preventBlurCounter += 1;
|
|
85
|
+
}
|
|
66
86
|
} else if (device.isDirectionUp(e)) {
|
|
67
87
|
newIndex = this.moveVerticaly('up');
|
|
88
|
+
if (preventBlurOnLongPress.up) {
|
|
89
|
+
this.preventBlurCounter += 1;
|
|
90
|
+
}
|
|
68
91
|
} else if (device.isDirectionDown(e)) {
|
|
69
92
|
newIndex = this.moveVerticaly('down');
|
|
93
|
+
if (preventBlurOnLongPress.down) {
|
|
94
|
+
this.preventBlurCounter += 1;
|
|
95
|
+
}
|
|
70
96
|
}
|
|
71
97
|
if (newIndex) {
|
|
72
98
|
stopEvent(e);
|
|
73
99
|
this.index = newIndex;
|
|
74
100
|
this.focusCurrentIndex();
|
|
101
|
+
} else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
|
|
102
|
+
stopEvent(e);
|
|
75
103
|
}
|
|
76
104
|
};
|
|
77
105
|
|
|
106
|
+
onKeyUp = () => {
|
|
107
|
+
this.preventBlurCounter = 0;
|
|
108
|
+
};
|
|
109
|
+
|
|
78
110
|
getRefAt({ x, y }: Index2D): Reference {
|
|
79
111
|
const { matrix } = this.props;
|
|
80
112
|
if (matrix[y] && matrix[y][x]) return matrix[y][x];
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { stopEvent } from '@24i/bigscreen-sdk/utils/stopEvent';
|
|
1
2
|
import { IFocusable } from '../../IFocusable';
|
|
2
3
|
import { getLayoutBase } from '../Base';
|
|
3
4
|
import { rootRef, refChildA, refChildB, refChildC, refChildD } from './shared';
|
|
4
5
|
|
|
6
|
+
jest.mock('@24i/bigscreen-sdk/utils/stopEvent');
|
|
7
|
+
|
|
5
8
|
const mockKeydown = jest.fn();
|
|
6
9
|
const mockEvent: any = {
|
|
7
10
|
target: null,
|
|
8
|
-
preventDefault: jest.fn(),
|
|
9
|
-
stopPropagation: jest.fn(),
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
const Base = getLayoutBase(mockKeydown);
|
|
@@ -15,18 +16,14 @@ describe('Focus Layout Base', () => {
|
|
|
15
16
|
let base: any;
|
|
16
17
|
beforeEach(() => {
|
|
17
18
|
base = new Base({
|
|
19
|
+
...Base.defaultProps,
|
|
18
20
|
domRef: rootRef,
|
|
19
21
|
children: 'children',
|
|
20
22
|
focusableChildren: [refChildA, refChildB, refChildC],
|
|
21
23
|
onFocusChanged: jest.fn(),
|
|
22
24
|
onBeforeFocusChange: jest.fn(),
|
|
23
25
|
});
|
|
24
|
-
|
|
25
|
-
refChildA.current!.focus.mockClear();
|
|
26
|
-
refChildB.current!.focus.mockClear();
|
|
27
|
-
refChildC.current!.focus.mockClear();
|
|
28
|
-
mockEvent.preventDefault.mockClear();
|
|
29
|
-
mockEvent.stopPropagation.mockClear();
|
|
26
|
+
jest.clearAllMocks();
|
|
30
27
|
});
|
|
31
28
|
|
|
32
29
|
it('should throw without domRef on componentDidMount', () => {
|
|
@@ -108,30 +105,43 @@ describe('Focus Layout Base', () => {
|
|
|
108
105
|
expect(refChildA.current!.focus).not.toHaveBeenCalled();
|
|
109
106
|
});
|
|
110
107
|
|
|
111
|
-
it('should call
|
|
108
|
+
it('should call stopEvent when going forward', () => {
|
|
112
109
|
base.forward(mockEvent);
|
|
113
|
-
expect(
|
|
114
|
-
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
|
110
|
+
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
115
111
|
});
|
|
116
112
|
|
|
117
|
-
it('should call
|
|
113
|
+
it('should call stopEvent when going backward', () => {
|
|
118
114
|
base.index = 1;
|
|
119
115
|
base.backward(mockEvent);
|
|
120
|
-
expect(
|
|
121
|
-
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
|
116
|
+
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
122
117
|
});
|
|
123
118
|
|
|
124
|
-
it('should not call
|
|
119
|
+
it('should not call stopEvent when can\'t go forward', () => {
|
|
125
120
|
base.index = 2;
|
|
126
121
|
base.forward(mockEvent);
|
|
127
|
-
expect(
|
|
128
|
-
|
|
122
|
+
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should not call stopEvent when can\'t go backward', () => {
|
|
126
|
+
base.backward(mockEvent);
|
|
127
|
+
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should call stopEvent when going forward and preventBlurOnLongPress is on', () => {
|
|
131
|
+
base.props.preventBlurOnLongPress.forward = true;
|
|
132
|
+
base.forward(mockEvent);
|
|
133
|
+
base.forward(mockEvent);
|
|
134
|
+
base.forward(mockEvent);
|
|
135
|
+
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
129
136
|
});
|
|
130
137
|
|
|
131
|
-
it('should
|
|
138
|
+
it('should call stopEvent when going backward and preventBlurOnLongPress is on', () => {
|
|
139
|
+
base.index = 2;
|
|
140
|
+
base.props.preventBlurOnLongPress.backward = true;
|
|
141
|
+
base.backward(mockEvent);
|
|
142
|
+
base.backward(mockEvent);
|
|
132
143
|
base.backward(mockEvent);
|
|
133
|
-
expect(
|
|
134
|
-
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
144
|
+
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
135
145
|
});
|
|
136
146
|
|
|
137
147
|
it('should call onFocusChanged when focusing current index', () => {
|
|
@@ -163,6 +173,7 @@ describe('Focus Layout Base', () => {
|
|
|
163
173
|
|
|
164
174
|
it('should take result of hasDom into account of determining current focus position', () => {
|
|
165
175
|
base = new Base({
|
|
176
|
+
...Base.defaultProps,
|
|
166
177
|
domRef: rootRef,
|
|
167
178
|
children: 'children',
|
|
168
179
|
focusableChildren: [refChildA, refChildB, refChildC, refChildD],
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createRef } from '@24i/bigscreen-sdk/jsx';
|
|
2
|
+
import { stopEvent } from '@24i/bigscreen-sdk/utils/stopEvent';
|
|
2
3
|
import { LEFT, RIGHT, UP, DOWN, Key } from '@24i/bigscreen-sdk/device/keymap';
|
|
3
4
|
import { Matrix, CircularDirection } from '../Matrix';
|
|
4
5
|
|
|
6
|
+
jest.mock('@24i/bigscreen-sdk/utils/stopEvent');
|
|
7
|
+
|
|
5
8
|
const simulateKey = (key: Key): KeyboardEvent => ({
|
|
6
9
|
code: key.code,
|
|
7
|
-
preventDefault: jest.fn(),
|
|
8
|
-
stopPropagation: jest.fn(),
|
|
9
10
|
} as unknown as KeyboardEvent);
|
|
10
11
|
|
|
11
12
|
const getFocusable = () => ({
|
|
@@ -35,6 +36,7 @@ describe('Matrix focus', () => {
|
|
|
35
36
|
|
|
36
37
|
beforeEach(() => {
|
|
37
38
|
matrix = new Matrix({ ...Matrix.defaultProps, matrix: MATRIX, domRef, children: [] });
|
|
39
|
+
jest.clearAllMocks();
|
|
38
40
|
});
|
|
39
41
|
|
|
40
42
|
it('should have default index at 0, 0', () => {
|
|
@@ -46,8 +48,7 @@ describe('Matrix focus', () => {
|
|
|
46
48
|
const event = simulateKey(RIGHT);
|
|
47
49
|
matrix.onKeyDown(event);
|
|
48
50
|
expect(B1.current!.focus).toHaveBeenCalled();
|
|
49
|
-
expect(
|
|
50
|
-
expect(event.stopPropagation).toHaveBeenCalled();
|
|
51
|
+
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
51
52
|
B1.current!.focus.mockClear();
|
|
52
53
|
});
|
|
53
54
|
|
|
@@ -56,8 +57,7 @@ describe('Matrix focus', () => {
|
|
|
56
57
|
const event = simulateKey(DOWN);
|
|
57
58
|
matrix.onKeyDown(event);
|
|
58
59
|
expect(A2.current!.focus).toHaveBeenCalled();
|
|
59
|
-
expect(
|
|
60
|
-
expect(event.stopPropagation).toHaveBeenCalled();
|
|
60
|
+
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
61
61
|
A2.current!.focus.mockClear();
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -66,8 +66,7 @@ describe('Matrix focus', () => {
|
|
|
66
66
|
const event = simulateKey(LEFT);
|
|
67
67
|
matrix.onKeyDown(event);
|
|
68
68
|
expect(C3.current!.focus).toHaveBeenCalled();
|
|
69
|
-
expect(
|
|
70
|
-
expect(event.stopPropagation).toHaveBeenCalled();
|
|
69
|
+
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
71
70
|
C3.current!.focus.mockClear();
|
|
72
71
|
});
|
|
73
72
|
|
|
@@ -76,8 +75,7 @@ describe('Matrix focus', () => {
|
|
|
76
75
|
const event = simulateKey(UP);
|
|
77
76
|
matrix.onKeyDown(event);
|
|
78
77
|
expect(C2.current!.focus).toHaveBeenCalled();
|
|
79
|
-
expect(
|
|
80
|
-
expect(event.stopPropagation).toHaveBeenCalled();
|
|
78
|
+
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
81
79
|
C2.current!.focus.mockClear();
|
|
82
80
|
});
|
|
83
81
|
|
|
@@ -86,8 +84,7 @@ describe('Matrix focus', () => {
|
|
|
86
84
|
const event = simulateKey(LEFT);
|
|
87
85
|
matrix.onKeyDown(event);
|
|
88
86
|
expect(matrix.index).toStrictEqual({ x: 0, y: 0 });
|
|
89
|
-
expect(
|
|
90
|
-
expect(event.stopPropagation).not.toHaveBeenCalled();
|
|
87
|
+
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
91
88
|
});
|
|
92
89
|
|
|
93
90
|
it('should not leave top boundary', () => {
|
|
@@ -95,8 +92,7 @@ describe('Matrix focus', () => {
|
|
|
95
92
|
const event = simulateKey(UP);
|
|
96
93
|
matrix.onKeyDown(event);
|
|
97
94
|
expect(matrix.index).toStrictEqual({ x: 1, y: 2 });
|
|
98
|
-
expect(
|
|
99
|
-
expect(event.stopPropagation).not.toHaveBeenCalled();
|
|
95
|
+
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
100
96
|
});
|
|
101
97
|
|
|
102
98
|
it('should not leave right boundary', () => {
|
|
@@ -104,8 +100,7 @@ describe('Matrix focus', () => {
|
|
|
104
100
|
const event = simulateKey(RIGHT);
|
|
105
101
|
matrix.onKeyDown(event);
|
|
106
102
|
expect(matrix.index).toStrictEqual({ x: 2, y: 1 });
|
|
107
|
-
expect(
|
|
108
|
-
expect(event.stopPropagation).not.toHaveBeenCalled();
|
|
103
|
+
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
109
104
|
});
|
|
110
105
|
|
|
111
106
|
it('should not leave bottom boundary', () => {
|
|
@@ -113,8 +108,51 @@ describe('Matrix focus', () => {
|
|
|
113
108
|
const event = simulateKey(DOWN);
|
|
114
109
|
matrix.onKeyDown(event);
|
|
115
110
|
expect(matrix.index).toStrictEqual({ x: 3, y: 2 });
|
|
116
|
-
expect(
|
|
117
|
-
|
|
111
|
+
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should call stopEvent when on left and preventBlurOnLongPress is on', () => {
|
|
115
|
+
matrix.props.preventBlurOnLongPress.left = true;
|
|
116
|
+
matrix.index = { x: 1, y: 0 };
|
|
117
|
+
const event = simulateKey(LEFT);
|
|
118
|
+
matrix.onKeyDown(event);
|
|
119
|
+
matrix.onKeyDown(event);
|
|
120
|
+
matrix.onKeyDown(event);
|
|
121
|
+
expect(matrix.index).toStrictEqual({ x: 0, y: 0 });
|
|
122
|
+
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should call stopEvent when on up and preventBlurOnLongPress is on', () => {
|
|
126
|
+
matrix.props.preventBlurOnLongPress.up = true;
|
|
127
|
+
matrix.index = { x: 0, y: 1 };
|
|
128
|
+
const event = simulateKey(UP);
|
|
129
|
+
matrix.onKeyDown(event);
|
|
130
|
+
matrix.onKeyDown(event);
|
|
131
|
+
matrix.onKeyDown(event);
|
|
132
|
+
expect(matrix.index).toStrictEqual({ x: 0, y: 0 });
|
|
133
|
+
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should call stopEvent when on right and preventBlurOnLongPress is on', () => {
|
|
137
|
+
matrix.props.preventBlurOnLongPress.right = true;
|
|
138
|
+
matrix.index = { x: 2, y: 0 };
|
|
139
|
+
const event = simulateKey(RIGHT);
|
|
140
|
+
matrix.onKeyDown(event);
|
|
141
|
+
matrix.onKeyDown(event);
|
|
142
|
+
matrix.onKeyDown(event);
|
|
143
|
+
expect(matrix.index).toStrictEqual({ x: 3, y: 0 });
|
|
144
|
+
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should call stopEvent when on down and preventBlurOnLongPress is on', () => {
|
|
148
|
+
matrix.props.preventBlurOnLongPress.down = true;
|
|
149
|
+
matrix.index = { x: 0, y: 1 };
|
|
150
|
+
const event = simulateKey(DOWN);
|
|
151
|
+
matrix.onKeyDown(event);
|
|
152
|
+
matrix.onKeyDown(event);
|
|
153
|
+
matrix.onKeyDown(event);
|
|
154
|
+
expect(matrix.index).toStrictEqual({ x: 0, y: 2 });
|
|
155
|
+
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
118
156
|
});
|
|
119
157
|
|
|
120
158
|
it('should keep row (Ax -> RIGHT -> B1 -> RIGHT -> Cx)', () => {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { processYear, dateFormatter, toNumericLocaleTimeString } from './common';
|
|
2
|
+
import { DateTimeFormatOptions, Day, Month, Weekday } from './types';
|
|
3
|
+
|
|
4
|
+
const HE_MONTHS = {
|
|
5
|
+
narrow: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
|
|
6
|
+
short: [
|
|
7
|
+
'ינו׳',
|
|
8
|
+
'פבר׳',
|
|
9
|
+
'מרץ',
|
|
10
|
+
'אפר׳',
|
|
11
|
+
'מאי',
|
|
12
|
+
'יוני',
|
|
13
|
+
'יולי',
|
|
14
|
+
'אוג׳',
|
|
15
|
+
'ספט׳',
|
|
16
|
+
'אוק׳',
|
|
17
|
+
'נוב׳',
|
|
18
|
+
'דצמ׳',
|
|
19
|
+
],
|
|
20
|
+
long: [
|
|
21
|
+
'ינואר',
|
|
22
|
+
'פברואר',
|
|
23
|
+
'מרץ',
|
|
24
|
+
'אפריל',
|
|
25
|
+
'מאי',
|
|
26
|
+
'יוני',
|
|
27
|
+
'יולי',
|
|
28
|
+
'אוגוסט',
|
|
29
|
+
'ספטמבר',
|
|
30
|
+
'אוקטובר',
|
|
31
|
+
'נובמבר',
|
|
32
|
+
'דצמבר',
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const HE_WEEKDAYS = {
|
|
37
|
+
narrow: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
|
|
38
|
+
short: ['יום א׳', 'יום ב׳', 'יום ג׳', 'יום ד׳', 'יום ה׳', 'יום ו׳', 'שבת'],
|
|
39
|
+
long: ['יום ראשון', 'יום שני', 'יום שלישי', 'יום רביעי', 'יום חמישי', 'יום שישי', 'יום שבת'],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const processWeekday = (
|
|
43
|
+
dateStringParts: string[],
|
|
44
|
+
weekday: Weekday | undefined,
|
|
45
|
+
date: Date,
|
|
46
|
+
) => {
|
|
47
|
+
if (typeof weekday === 'undefined') return;
|
|
48
|
+
const weekdayString = HE_WEEKDAYS[weekday][date.getDay()];
|
|
49
|
+
dateStringParts.push(`${weekdayString},`);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const processDay = (dateParts: string[], day: Day | undefined, date: Date) => {
|
|
53
|
+
if (typeof day === 'undefined') return;
|
|
54
|
+
const strDate = date.getDate().toString();
|
|
55
|
+
dateParts.push(strDate);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const processMonth = (
|
|
59
|
+
dateParts: string[],
|
|
60
|
+
day:Day | undefined,
|
|
61
|
+
month: Month | undefined,
|
|
62
|
+
date: Date,
|
|
63
|
+
) => {
|
|
64
|
+
if (typeof month === 'undefined') return;
|
|
65
|
+
if (month === '2-digit' || month === 'numeric') {
|
|
66
|
+
const strMonth = (date.getMonth() + 1).toString();
|
|
67
|
+
dateParts.push(strMonth);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const finalMonth = HE_MONTHS[month][date.getMonth()];
|
|
71
|
+
if (day != null) {
|
|
72
|
+
dateParts.push(`ב${finalMonth}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
dateParts.push(finalMonth);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const toLocaleDateString = (date: Date) => (
|
|
79
|
+
{ weekday, year, month, day }: DateTimeFormatOptions = {
|
|
80
|
+
year: 'numeric', month: '2-digit', day: '2-digit',
|
|
81
|
+
},
|
|
82
|
+
) => {
|
|
83
|
+
const dateStringParts: string[] = [];
|
|
84
|
+
processWeekday(dateStringParts, weekday, date);
|
|
85
|
+
const dateConnection = month === 'numeric' || month === '2-digit' ? '.' : '';
|
|
86
|
+
const dateParts: string[] = [];
|
|
87
|
+
processDay(dateParts, day, date);
|
|
88
|
+
if ((month === 'narrow' ||
|
|
89
|
+
month === 'short' ||
|
|
90
|
+
month === 'long') &&
|
|
91
|
+
day != null
|
|
92
|
+
) {
|
|
93
|
+
dateParts.push(' ');
|
|
94
|
+
}
|
|
95
|
+
processMonth(dateParts, day, month, date);
|
|
96
|
+
if (month === 'long') dateParts.push(' ');
|
|
97
|
+
processYear(dateParts, year, date);
|
|
98
|
+
dateStringParts.push(dateParts.join(dateConnection));
|
|
99
|
+
return dateStringParts.join(' ');
|
|
100
|
+
};
|
|
101
|
+
export const heDateFormatter = dateFormatter(toNumericLocaleTimeString, toLocaleDateString);
|
|
@@ -3,6 +3,7 @@ import { csDateFormatter } from './date/cs';
|
|
|
3
3
|
import { deDateFormatter } from './date/de';
|
|
4
4
|
import { esDateFormatter } from './date/es';
|
|
5
5
|
import { frDateFormatter } from './date/fr';
|
|
6
|
+
import { heDateFormatter } from './date/he';
|
|
6
7
|
import { isoDateFormatter } from './date/iso';
|
|
7
8
|
import { Formatters } from './types';
|
|
8
9
|
|
|
@@ -27,6 +28,7 @@ class L10n implements IEvents<Events> {
|
|
|
27
28
|
fr: frDateFormatter,
|
|
28
29
|
es: esDateFormatter,
|
|
29
30
|
de: deDateFormatter,
|
|
31
|
+
he: heDateFormatter,
|
|
30
32
|
},
|
|
31
33
|
};
|
|
32
34
|
|