# @workshack/input Workshack input components library with gamepad and keyboard navigation support for Angular applications. ## Installation ```bash npm install @workshack/input ``` ## Services ### InputService **Description:** Core service for managing input devices, actions, and schemes. **Usage:** ```typescript import { InputService } from '@workshack/input'; @Component({ // ... }) export class MyComponent { constructor(private inputService: InputService) { // Define custom actions const jumpAction = this.inputService.defineAction('jump'); jumpAction.onDown(() => console.log('Jump pressed!')); jumpAction.onUp(() => console.log('Jump released!')); // Create input scheme const gameScheme = this.inputService.defineScheme('game'); // Listen for device changes this.inputService.deviceListChanged.subscribe(devices => { console.log('Available devices:', devices); }); } } ``` **Methods:** - `defineAction(name: string): InputAction` - Creates a new input action - `defineScheme(name: string): InputScheme` - Creates a new input scheme - `getDevices(): InputDevice[]` - Returns list of available input devices **Properties:** - `actions: InputAction[]` - Array of defined actions - `schemes: InputScheme[]` - Array of defined schemes - `devices: InputDevice[]` - Array of connected devices - `deviceListChanged: BehaviorSubject` - Observable for device changes --- ### ControlService (Example Integration) **Description:** Higher-level service that integrates with InputService to provide common navigation actions. **Usage:** ```typescript import { ControlService } from 'your-app/services/control.service'; @Component({ // ... }) export class MyComponent { constructor(private controlService: ControlService) { // Service automatically sets up common actions: // - Left, Right, Up, Down navigation // - Accept, Back, Exit actions // - Hint/Help action // Get key names for display const keyNames = this.controlService.getKeyNames('Enter', 'Escape'); console.log('Key names:', keyNames); // ['Enter', 'Escape'] or themed names } } ``` **Static Properties:** - `keyLeft: InputAction` - Left navigation action - `keyRight: InputAction` - Right navigation action - `keyUp: InputAction` - Up navigation action - `keyDown: InputAction` - Down navigation action - `keyAccept: InputAction` - Accept/confirm action - `keyBack: InputAction` - Back/cancel action - `keyExit: InputAction` - Exit action - `keyHint: InputAction` - Hint/help action **Methods:** - `getKeyNames(...names): string[]` - Get themed key names for display - `clearKeyBindings()` - Clear all key bindings from schemes - `bindActionsToScheme(device: InputDevice)` - Bind actions to device scheme ## Directives ### ActionBindingDirective **Selector:** `[inputActionBinding]` **Description:** Binds an action name to an HTML element. **Usage:** ```html
Back
``` **Properties:** - `inputActionBinding: string` - Action name to bind to the element **Features:** - Automatically sets `action` attribute on the element - Works with keyboard and gamepad navigation --- ### KeyItemDirective **Selector:** `[inputKeyItem]` **Description:** Makes an element navigable with keyboard/gamepad and handles focus states. **Usage:** ```html ``` **Properties:** - `active: string` - CSS class name for active/focused state (default: 'active') - `actionBindings: ActionBindings` - Object mapping action names to callback functions **Events:** - `activated: EventEmitter` - Emitted when item is activated - `deactivated: EventEmitter` - Emitted when item is deactivated - `focused: EventEmitter` - Emitted when item receives focus - `blurred: EventEmitter` - Emitted when item loses focus **Static Methods:** - `FireAction(action: string): boolean` - Fires action on currently focused item - `ActivateCurrentItem()` - Activates the currently focused item - `SelectNexItem(fromAngle: number, toAngle: number)` - Selects next item in direction --- ### KeyItemGroupDirective **Selector:** `[inputKeyItemGroup]` **Description:** Groups KeyItem elements for scoped navigation. **Usage:** ```html
``` **Features:** - Navigation is scoped to items within the group - Automatically manages focus within the group - Supports nested groups ## Models ### InputAction **Description:** Represents a bindable input action (e.g., "jump", "fire", "menu"). **Usage:** ```typescript const jumpAction = new InputAction('jump'); // Bind callbacks jumpAction.onDown((value, data, angle, timePressed) => { console.log('Jump started!', { value, data, angle, timePressed }); }); jumpAction.onUp((value, data, angle, timePressed) => { console.log('Jump ended!', { value, data, angle, timePressed }); }); jumpAction.onChange((value, data, angle, timePressed) => { console.log('Jump value changed:', value); }); // Fire the action jumpAction.fire(1.0); // Press jumpAction.fire(0.0); // Release ``` **Methods:** - `onDown(callback: ActionCallback)` - Register callback for action press - `onUp(callback: ActionCallback)` - Register callback for action release - `onChange(callback: ActionCallback)` - Register callback for value changes - `fire(value: number, data?: unknown, angle?: number, timePressed?: number)` - Fire the action - `mapKeysToScheme(scheme: InputScheme, keys: (string|number)[])` - Map keys to scheme --- ### InputDevice **Description:** Base class for input devices (keyboard, gamepad). **Properties:** - `name: string` - Device name - `type: string` - Device type ('keyboard', 'gamepad') **Methods:** - `hasScheme(): boolean` - Check if device has an input scheme - `mapKey(action: InputAction, keys: (string|number)[])` - Map keys to action --- ### InputKeyboard **Description:** Keyboard input device implementation. **Usage:** ```typescript const keyboard = new InputKeyboard('Main Keyboard'); keyboard.mapKey(jumpAction, ['Space', 'KeyW']); ``` --- ### InputGamepad **Description:** Gamepad input device implementation with automatic detection. **Static Properties:** - `DeviceConnected: Subject` - Observable for gamepad connections - `DeviceDisconnected: Subject` - Observable for gamepad disconnections **Static Methods:** - `Init()` - Initialize gamepad detection **Usage:** ```typescript // Listen for gamepad events InputGamepad.DeviceConnected.subscribe(gamepad => { console.log('Gamepad connected:', gamepad.name); gamepad.mapKey(jumpAction, [0]); // Map to button 0 }); InputGamepad.DeviceDisconnected.subscribe(gamepad => { console.log('Gamepad disconnected:', gamepad.name); }); // Initialize detection InputGamepad.Init(); ``` ## Complete Example ```typescript import { Component, OnInit } from '@angular/core'; import { InputService, NavigationModule, KeyItemDirective, KeyItemGroupDirective, ActionBindingDirective } from '@workshack/input'; @Component({ selector: 'app-game-menu', imports: [ NavigationModule, KeyItemDirective, KeyItemGroupDirective, ActionBindingDirective ], template: `

Game Menu

Use arrow keys or gamepad to navigate Press Enter or A to select
`, styles: [` .game-menu { display: flex; flex-direction: column; align-items: center; gap: 1rem; padding: 2rem; } .menu-item { padding: 1rem 2rem; background: #333; color: white; border: 2px solid transparent; border-radius: 8px; cursor: pointer; transition: all 0.2s; } .menu-item.active, .menu-item:hover { background: #555; border-color: #007bff; transform: scale(1.05); } .controls-hint { display: flex; flex-direction: column; text-align: center; font-size: 0.8rem; color: #666; margin-top: 2rem; } `] }) export class GameMenuComponent implements OnInit { constructor(private inputService: InputService) {} ngOnInit() { // Set up custom actions const menuAction = this.inputService.defineAction('menu'); menuAction.onUp(() => this.toggleMenu()); // Listen for device changes this.inputService.deviceListChanged.subscribe(devices => { console.log('Input devices:', devices.map(d => d.name)); }); } startGame() { console.log('Starting game...'); } showOptions() { console.log('Showing options...'); } exitGame() { console.log('Exiting game...'); } goBack() { console.log('Going back...'); return false; // Prevent default navigation } playHoverSound() { // Play hover sound effect console.log('Hover sound'); } toggleMenu() { console.log('Toggle menu'); } } ``` ## Features - 🎮 **Gamepad Support** - Automatic detection and mapping for standard gamepads - ⌨️ **Keyboard Navigation** - Full keyboard navigation with customizable key bindings - 🖱️ **Mouse/Touch Support** - Works seamlessly with mouse and touch interactions - 🎯 **Action Binding System** - Flexible system for binding actions to inputs - 📐 **Directional Navigation** - Smart directional navigation based on element positions - 🎨 **Customizable Themes** - Support for different key name themes (PlayStation, Xbox, etc.) - 🔄 **Hot-plugging** - Automatic detection of connected/disconnected devices - 🎛️ **Multiple Schemes** - Support for multiple input schemes per device ## Development ### Building ```bash npm run build:input ``` ### Watching for changes ```bash npm run watch:input ``` ### Testing ```bash npm run test:input ``` ### Publishing ```bash npm run publish:input ``` ## License This library is licensed under a custom license that allows: - **Free use** for non-commercial applications (no revenue, no ads, no paid features) - **Commercial use** with attribution requirement - you must credit the library in your app (e.g., on an "About" or "For Developers" page) For full license terms, see the [LICENSE](LICENSE) file. ### Attribution Example for Commercial Use ``` This application uses Workshack Input Library (https://www.npmjs.com/package/@workshack/input) © 2025 Workshack Team ```