|
||
---|---|---|
.angular/cache/20.1.3/ng-packagr | ||
src | ||
.gitignore | ||
.npmignore | ||
LICENSE | ||
README.md | ||
angular.json | ||
ng-package.json | ||
package.json | ||
tsconfig.lib.json | ||
tsconfig.lib.prod.json | ||
tsconfig.spec.json |
README.md
@workshack/input
Workshack input components library with gamepad and keyboard navigation support for Angular applications.
Installation
npm install @workshack/input
Services
InputService
Description: Core service for managing input devices, actions, and schemes.
Usage:
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 actiondefineScheme(name: string): InputScheme
- Creates a new input schemegetDevices(): InputDevice[]
- Returns list of available input devices
Properties:
actions: InputAction[]
- Array of defined actionsschemes: InputScheme[]
- Array of defined schemesdevices: InputDevice[]
- Array of connected devicesdeviceListChanged: BehaviorSubject<InputDevice[]>
- Observable for device changes
ControlService (Example Integration)
Description: Higher-level service that integrates with InputService to provide common navigation actions.
Usage:
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 actionkeyRight: InputAction
- Right navigation actionkeyUp: InputAction
- Up navigation actionkeyDown: InputAction
- Down navigation actionkeyAccept: InputAction
- Accept/confirm actionkeyBack: InputAction
- Back/cancel actionkeyExit: InputAction
- Exit actionkeyHint: InputAction
- Hint/help action
Methods:
getKeyNames(...names): string[]
- Get themed key names for displayclearKeyBindings()
- Clear all key bindings from schemesbindActionsToScheme(device: InputDevice)
- Bind actions to device scheme
Directives
ActionBindingDirective
Selector: [inputActionBinding]
Description: Binds an action name to an HTML element.
Usage:
<button inputActionBinding="confirm">Confirm</button>
<div inputActionBinding="back">Back</div>
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:
<button
inputKeyItem
[active]="'focused'"
[actionBindings]="{
Accept: () => onClick(),
Back: () => onCancel()
}"
(activated)="onActivated()"
(focused)="onFocused()"
(blurred)="onBlurred()">
Click me
</button>
Properties:
active: string
- CSS class name for active/focused state (default: 'active')actionBindings: ActionBindings
- Object mapping action names to callback functions
Events:
activated: EventEmitter<void>
- Emitted when item is activateddeactivated: EventEmitter<void>
- Emitted when item is deactivatedfocused: EventEmitter<void>
- Emitted when item receives focusblurred: EventEmitter<void>
- Emitted when item loses focus
Static Methods:
FireAction(action: string): boolean
- Fires action on currently focused itemActivateCurrentItem()
- Activates the currently focused itemSelectNexItem(fromAngle: number, toAngle: number)
- Selects next item in direction
KeyItemGroupDirective
Selector: [inputKeyItemGroup]
Description: Groups KeyItem elements for scoped navigation.
Usage:
<div inputKeyItemGroup>
<button inputKeyItem>Button 1</button>
<button inputKeyItem>Button 2</button>
<button inputKeyItem>Button 3</button>
</div>
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:
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 pressonUp(callback: ActionCallback)
- Register callback for action releaseonChange(callback: ActionCallback)
- Register callback for value changesfire(value: number, data?: unknown, angle?: number, timePressed?: number)
- Fire the actionmapKeysToScheme(scheme: InputScheme, keys: (string|number)[])
- Map keys to scheme
InputDevice
Description: Base class for input devices (keyboard, gamepad).
Properties:
name: string
- Device nametype: string
- Device type ('keyboard', 'gamepad')
Methods:
hasScheme(): boolean
- Check if device has an input schememapKey(action: InputAction, keys: (string|number)[])
- Map keys to action
InputKeyboard
Description: Keyboard input device implementation.
Usage:
const keyboard = new InputKeyboard('Main Keyboard');
keyboard.mapKey(jumpAction, ['Space', 'KeyW']);
InputGamepad
Description: Gamepad input device implementation with automatic detection.
Static Properties:
DeviceConnected: Subject<InputGamepad>
- Observable for gamepad connectionsDeviceDisconnected: Subject<InputGamepad>
- Observable for gamepad disconnections
Static Methods:
Init()
- Initialize gamepad detection
Usage:
// 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
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: `
<div class="game-menu" inputKeyItemGroup>
<h2>Game Menu</h2>
<button
inputKeyItem
[actionBindings]="{
Accept: () => startGame(),
Back: () => goBack()
}"
(focused)="playHoverSound()"
class="menu-item">
Start Game
</button>
<button
inputKeyItem
[actionBindings]="{
Accept: () => showOptions(),
Back: () => goBack()
}"
(focused)="playHoverSound()"
class="menu-item">
Options
</button>
<button
inputKeyItem
[actionBindings]="{
Accept: () => exitGame(),
Back: () => goBack()
}"
(focused)="playHoverSound()"
class="menu-item">
Exit
</button>
<div class="controls-hint">
<span>Use arrow keys or gamepad to navigate</span>
<span>Press Enter or A to select</span>
</div>
</div>
`,
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
npm run build:input
Watching for changes
npm run watch:input
Testing
npm run test:input
Publishing
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 file.
Attribution Example for Commercial Use
This application uses Workshack Input Library
(https://www.npmjs.com/package/@workshack/input)
© 2025 Workshack Team