ts-spatial-navigation 1.0.0-beta.1

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.
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Spatial Navigation Type Definitions
3
+ * Following Interface Segregation Principle (ISP)
4
+ */
5
+ import type { EnterToValue, RestrictModeValue } from './constants';
6
+ /**
7
+ * Navigation direction
8
+ */
9
+ export type Direction = 'up' | 'down' | 'left' | 'right';
10
+ /**
11
+ * Selector type - can be CSS selector string, NodeList, or HTMLElement
12
+ */
13
+ export type Selector = string | NodeList | HTMLElement | HTMLElement[];
14
+ /**
15
+ * Extended selector - includes @sectionId syntax
16
+ */
17
+ export type ExtendedSelector = Selector | `@${string}`;
18
+ /**
19
+ * Point in 2D space
20
+ */
21
+ export interface Point {
22
+ x: number;
23
+ y: number;
24
+ }
25
+ /**
26
+ * Rectangle with center point calculation
27
+ */
28
+ export interface Rect {
29
+ left: number;
30
+ top: number;
31
+ right: number;
32
+ bottom: number;
33
+ width: number;
34
+ height: number;
35
+ element: HTMLElement;
36
+ center: {
37
+ x: number;
38
+ y: number;
39
+ left: number;
40
+ right: number;
41
+ top: number;
42
+ bottom: number;
43
+ };
44
+ }
45
+ /**
46
+ * Distance calculation function
47
+ */
48
+ export type DistanceFunction = (rect: Rect) => number;
49
+ /**
50
+ * Collection of distance functions for navigation
51
+ */
52
+ export interface DistanceFunctions {
53
+ nearPlumbLineIsBetter: DistanceFunction;
54
+ nearHorizonIsBetter: DistanceFunction;
55
+ nearTargetLeftIsBetter: DistanceFunction;
56
+ nearTargetTopIsBetter: DistanceFunction;
57
+ topIsBetter: DistanceFunction;
58
+ bottomIsBetter: DistanceFunction;
59
+ leftIsBetter: DistanceFunction;
60
+ rightIsBetter: DistanceFunction;
61
+ }
62
+ /**
63
+ * Priority group with distance functions for sorting
64
+ */
65
+ export interface Priority {
66
+ group: Rect[];
67
+ distance: DistanceFunction[];
68
+ }
69
+ /**
70
+ * Array of priority groups
71
+ */
72
+ export type Priorities = Priority[];
73
+ /**
74
+ * Basic section identification
75
+ */
76
+ export interface SectionIdentity {
77
+ /**
78
+ * Unique identifier for the section
79
+ */
80
+ id?: string;
81
+ /**
82
+ * CSS selector for navigable elements in this section
83
+ */
84
+ selector?: string;
85
+ }
86
+ /**
87
+ * Navigation behavior configuration
88
+ */
89
+ export interface NavigationBehavior {
90
+ /**
91
+ * Only allow navigation in cardinal directions (no diagonal)
92
+ * @default false
93
+ */
94
+ straightOnly?: boolean;
95
+ /**
96
+ * Threshold for straight direction overlap (0 to 1)
97
+ * @default 0.5
98
+ */
99
+ straightOverlapThreshold?: number;
100
+ /**
101
+ * Remember previously focused element when navigating back
102
+ * @default false
103
+ */
104
+ rememberSource?: boolean;
105
+ }
106
+ /**
107
+ * Section transition configuration
108
+ */
109
+ export interface SectionTransition {
110
+ /**
111
+ * Behavior when entering this section
112
+ * - 'last-focused': Focus the last focused element
113
+ * - 'default-element': Focus the default element
114
+ * - '': Natural focus behavior
115
+ * @default ''
116
+ */
117
+ enterTo?: EnterToValue | 'last-focused' | 'default-element' | '';
118
+ /**
119
+ * Configuration for leaving the section in specific directions
120
+ */
121
+ leaveFor?: Partial<LeaveFor>;
122
+ /**
123
+ * How to restrict navigation at section boundaries
124
+ * - 'self-first': Prefer staying in section
125
+ * - 'self-only': Never leave section via keyboard
126
+ * - 'none': No restriction
127
+ * @default 'self-first'
128
+ */
129
+ restrict?: RestrictModeValue | 'self-first' | 'self-only' | 'none';
130
+ }
131
+ /**
132
+ * LeaveFor configuration - what to focus when leaving in each direction
133
+ */
134
+ export interface LeaveFor {
135
+ up?: string | HTMLElement;
136
+ down?: string | HTMLElement;
137
+ left?: string | HTMLElement;
138
+ right?: string | HTMLElement;
139
+ }
140
+ /**
141
+ * Filter configuration for navigable elements
142
+ */
143
+ export interface FilterConfig {
144
+ /**
145
+ * Custom filter function for navigable elements
146
+ * Return false to exclude an element from navigation
147
+ */
148
+ navigableFilter?: NavigableFilter;
149
+ /**
150
+ * Elements matching this selector will not have tabindex modified
151
+ * @default 'a, input, select, textarea, button, iframe, [contentEditable=true]'
152
+ */
153
+ tabIndexIgnoreList?: string;
154
+ /**
155
+ * Whether to ignore hidden elements
156
+ * @default false
157
+ */
158
+ ignoreHidden?: boolean;
159
+ }
160
+ /**
161
+ * Navigable filter function signature
162
+ */
163
+ export type NavigableFilter = (element: HTMLElement, sectionId: string) => boolean;
164
+ /**
165
+ * Section state (internal)
166
+ */
167
+ export interface SectionState {
168
+ /**
169
+ * Whether this section is disabled
170
+ */
171
+ disabled?: boolean;
172
+ /**
173
+ * Default element to focus when entering section
174
+ */
175
+ defaultElement?: HTMLElement | string;
176
+ /**
177
+ * Last focused element in this section
178
+ */
179
+ lastFocusedElement?: HTMLElement;
180
+ /**
181
+ * Previous navigation state for rememberSource
182
+ */
183
+ previous?: PreviousFocus;
184
+ }
185
+ /**
186
+ * Previous focus state for rememberSource feature
187
+ */
188
+ export interface PreviousFocus {
189
+ target: HTMLElement;
190
+ destination: HTMLElement;
191
+ reverse: Direction;
192
+ }
193
+ /**
194
+ * Full section configuration
195
+ * Combines all configuration interfaces
196
+ */
197
+ export interface SectionConfig extends SectionIdentity, NavigationBehavior, SectionTransition, FilterConfig, SectionState {
198
+ }
199
+ /**
200
+ * Configuration for adding a new section
201
+ * All properties optional except selector
202
+ */
203
+ export interface AddSectionConfig extends Partial<SectionConfig> {
204
+ selector: string;
205
+ }
206
+ /**
207
+ * Global configuration
208
+ */
209
+ export interface GlobalConfig extends Omit<SectionConfig, 'id' | 'lastFocusedElement' | 'previous'> {
210
+ /**
211
+ * Prefix for auto-generated section IDs
212
+ * @default 'section-'
213
+ */
214
+ sectionPrefix?: string;
215
+ /**
216
+ * Prefix for custom events
217
+ * @default 'sn:'
218
+ */
219
+ eventPrefix?: string;
220
+ }
221
+ /**
222
+ * Legacy Config type for backwards compatibility
223
+ * @deprecated Use SectionConfig instead
224
+ */
225
+ export type Config = SectionConfig;
226
+ /**
227
+ * Detail object for spatial navigation events
228
+ */
229
+ export interface SpatialEventDetail {
230
+ /**
231
+ * Direction of navigation
232
+ */
233
+ direction?: Direction;
234
+ /**
235
+ * Current section ID
236
+ */
237
+ id?: string;
238
+ /**
239
+ * Next section ID (for cross-section navigation)
240
+ */
241
+ nextId?: string;
242
+ /**
243
+ * Element that will receive focus
244
+ */
245
+ nextElement?: HTMLElement;
246
+ /**
247
+ * Element that had focus before
248
+ */
249
+ previousElement?: HTMLElement;
250
+ /**
251
+ * Whether triggered by native focus events
252
+ */
253
+ native?: boolean;
254
+ /**
255
+ * What caused this navigation
256
+ */
257
+ cause?: 'keydown' | 'api';
258
+ }
259
+ /**
260
+ * Spatial navigation custom event
261
+ */
262
+ export interface SpatialEvent extends CustomEvent<SpatialEventDetail> {
263
+ type: `sn:${string}`;
264
+ }
265
+ /**
266
+ * Section store - Map of section ID to configuration
267
+ */
268
+ export type SectionStore = Map<string, SectionConfig>;
269
+ /**
270
+ * Navigation state
271
+ */
272
+ export interface NavigationState {
273
+ ready: boolean;
274
+ paused: boolean;
275
+ duringFocusChange: boolean;
276
+ sections: SectionStore;
277
+ sectionCount: number;
278
+ defaultSectionId: string;
279
+ lastSectionId: string;
280
+ idPool: number;
281
+ }
282
+ /**
283
+ * Deep partial type
284
+ */
285
+ export type DeepPartial<T> = {
286
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
287
+ };
288
+ /**
289
+ * Make specific properties required
290
+ */
291
+ export type WithRequired<T, K extends keyof T> = T & {
292
+ [P in K]-?: T[P];
293
+ };
294
+ /**
295
+ * Extract non-undefined properties
296
+ */
297
+ export type NonUndefined<T> = T extends undefined ? never : T;
298
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Spatial Navigation Type Definitions
3
+ * Following Interface Segregation Principle (ISP)
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "ts-spatial-navigation",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "A TypeScript library for spatial navigation in Smart TV applications",
5
+ "author": "Rodrigo França <rodrigo@example.com>",
6
+ "license": "MPL-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/rodrigofranca/ts-spatial-navigation.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/rodrigofranca/ts-spatial-navigation/issues"
13
+ },
14
+ "homepage": "https://github.com/rodrigofranca/ts-spatial-navigation#readme",
15
+ "keywords": [
16
+ "spatial-navigation",
17
+ "smart-tv",
18
+ "tv",
19
+ "navigation",
20
+ "focus",
21
+ "keyboard-navigation",
22
+ "typescript",
23
+ "webos",
24
+ "tizen",
25
+ "android-tv",
26
+ "fire-tv",
27
+ "arrow-keys"
28
+ ],
29
+ "type": "module",
30
+ "main": "./dist/spatial-navigation.js",
31
+ "module": "./dist/spatial-navigation.js",
32
+ "types": "./dist/spatial-navigation.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/spatial-navigation.d.ts",
36
+ "import": "./dist/spatial-navigation.js",
37
+ "default": "./dist/spatial-navigation.js"
38
+ },
39
+ "./core": {
40
+ "types": "./dist/core.d.ts",
41
+ "import": "./dist/core.js"
42
+ },
43
+ "./geometry": {
44
+ "types": "./dist/geometry.d.ts",
45
+ "import": "./dist/geometry.js"
46
+ },
47
+ "./constants": {
48
+ "types": "./dist/constants.d.ts",
49
+ "import": "./dist/constants.js"
50
+ },
51
+ "./strategies": {
52
+ "types": "./dist/strategies.d.ts",
53
+ "import": "./dist/strategies.js"
54
+ },
55
+ "./state": {
56
+ "types": "./dist/state.d.ts",
57
+ "import": "./dist/state.js"
58
+ }
59
+ },
60
+ "files": [
61
+ "dist",
62
+ "README.md",
63
+ "LICENSE"
64
+ ],
65
+ "sideEffects": false,
66
+ "engines": {
67
+ "node": ">=16.0.0"
68
+ },
69
+ "devDependencies": {
70
+ "@types/node": "^20.11.5",
71
+ "@vitest/ui": "^4.0.18",
72
+ "happy-dom": "^20.5.0",
73
+ "typescript": "^4.9.5",
74
+ "vitest": "^4.0.18"
75
+ },
76
+ "scripts": {
77
+ "build": "tsc --build",
78
+ "build:clean": "rm -rf dist && tsc --build --force",
79
+ "test": "vitest run",
80
+ "test:watch": "vitest",
81
+ "test:ui": "vitest --ui",
82
+ "test:coverage": "vitest run --coverage",
83
+ "lint": "tsc --noEmit"
84
+ }
85
+ }