amjadiqbal / laravel-ninja-keys
Laravel integration for the ninja-keys web component with a fluent PHP API and Blade directive.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/amjadiqbal/laravel-ninja-keys
Requires
- php: ^8.1
- illuminate/support: ^10.0|^11.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
This package is auto-updated.
Last update: 2026-02-23 00:01:51 UTC
README
Laravel integration for the ninja-keys web component. Ships a fluent PHP API, a Blade directive for rendering and JS-bridge logic, and Gate-based authorization filtering for server-side visibility.
Table of Contents
- Overview
- Requirements
- Installation
- Quickstart
- Configuration
- Rendering Options
- Fluent API
- Authorization
- Event Hooks
- Styling
- Data Model Mapping
- Testing
- Versioning
- Contributing
- Security
- License
- Credits
Overview
- Keyboard command palette powered by ninja-keys (Web Component).
- Laravel-friendly API for building actions via PHP.
- Server-side filtering using Gate abilities (hide unauthorized actions).
- CDN or local asset loading; Material Icons support.
- Theme handling (auto/dark/light) and optional footer control.
- Works with flat or tree action data structures.
- Bridges string handlers to global window functions safely.
Requirements
- PHP: ^8.1
- Laravel: ^10.0 or ^11.0
Installation
composer require amjadiqbal/laravel-ninja-keys
Auto-discovery registers the service provider and facade.
If you use Pest for tests:
composer config --no-plugins allow-plugins.pestphp/pest-plugin true
Quickstart
Publish config:
php artisan vendor:publish --tag=config
Add to your layout:
@ninjaKeysScripts
Register actions:
use AmjadIqbal\NinjaKeys\Facades\NinjaKeys; NinjaKeys::addAction('create-post') ->title('New Post') ->description('Create a post') ->shortcut('ctrl+n') // normalized to JS hotkey ->parent('posts') ->mdIcon('note_add') ->handler('onCreatePost') // window.onCreatePost(detail) will be called ->can(['create-post', 'manage-posts']); // visible if any ability allows
Add global handlers:
<script> window.onCreatePost = (detail) => { /* your logic */ }; </script>
Configuration
Key options (config/ninja-keys.php):
- use_cdn (bool) — load from CDN; otherwise asset_path
- cdn_url (string) — default https://unpkg.com/ninja-keys?module
- asset_path (string|null) — published asset path if not using CDN
- material_icons_url (string|null) — Google Fonts URL to load Material Icons
- placeholder, searchPlaceholder (string|null) — search input placeholders
- disableHotkeys, hideBreadcrumbs, hotKeysJoinedView, noAutoLoadMdIcons (bool)
- openHotkey, hotKeys (string) — hotKeys is an alias for openHotkey
- navigationUpHotkey, navigationDownHotkey, closeHotkey, goBackHotkey, selectHotkey (string)
- theme (light|dark|auto)
- noHeader, noFooter (bool)
- onChange (string|null) — window function name to receive change events
- onOpen (string|null) — window function name called before ninja.open()
Publish and edit:
php artisan vendor:publish --tag=config
Rendering Options
- Blade directive
@ninjaKeysScripts
- Blade component (optional)
- Component class exists at
src/Components/NinjaKeys.php - Register alias manually if desired:
// in a service provider \Illuminate\Support\Facades\Blade::component('ninja-keys', \AmjadIqbal\NinjaKeys\Components\NinjaKeys::class);
- Component class exists at
Load from local asset instead of CDN
Publish assets to your public directory (or bundle in your build), then set:
// config/ninja-keys.php 'use_cdn' => false, 'asset_path' => '/assets/js/ninja-keys.js', // example path
Fluent API
use AmjadIqbal\NinjaKeys\Facades\NinjaKeys; NinjaKeys::addActions([ ['id' => 'Theme', 'title' => 'Change theme...', 'hotkey' => 'ctrl+t', 'children' => ['Light', 'Dark', 'System']], ['id' => 'Light', 'title' => 'Light Theme', 'parent' => 'Theme', 'mdIcon' => 'light_mode', 'handler' => 'setLightTheme'], ['id' => 'Dark', 'title' => 'Dark Theme', 'parent' => 'Theme', 'mdIcon' => 'dark_mode', 'handler' => 'setDarkTheme'], ]);
Convenience fields:
- href(string) and target(string) are included for your own handlers/UI logic (they are not executed automatically by the component).
Authorization
- Use
can(string|array)to attach Gate abilities. - If any provided ability allows, the action is included; otherwise excluded server-side.
- Gate definitions in tests or app boot:
Gate::define('create-post', fn ($user = null) => true);
Event Hooks
- onChange: listen to component change and receive
{ search, actions }. - onOpen: called before
ninja.open(...)with{ args }. - Handler bridging:
- If an action’s
handleris a string (e.g.,"myFunction"), the directive listens toselectedand callswindow[handler](event.detail)when present. - Recommended: keep handlers small and side-effect free; heavy logic can be deferred.
- If an action’s
Styling
- Theme:
theme = auto: toggles dark class based onprefers-color-scheme.theme = dark: forcesclass="dark"on the component.
- Footer:
noFooter = truerenders an empty footer slot.
- Material Icons:
- Load via
material_icons_urlor disable withnoAutoLoadMdIcons.
- Load via
- CSS Shadow Parts (from ninja-keys):
actions-list,ninja-action,ninja-selected,ninja-input,ninja-input-wrapper- Example:
ninja-keys::part(ninja-action) { border-radius: 8px; }
CSS Variables (examples)
ninja-keys { --ninja-width: 640px; --ninja-accent-color: #6e5ed2; --ninja-icon-size: 1.2em; --ninja-selected-background: #f8f9fb; }
Data Model Mapping
ActionBuilder → Ninja Keys fields:
- addAction(id) → id
- title(string) → title
- description(string) → description
- icon(string) → icon (HTML/SVG)
- mdIcon(string) → mdIcon (Material icon name)
- parent(string) → parent
- children(array) → children
- shortcut(string) → normalized to hotkey
- hotkey(string) → hotkey
- section(string) → section
- keywords(string) → keywords
- handler(string) → handler (global window function name)
- href(string), target(string) → convenience extras (not auto-executed)
- can(string|array) → Gate abilities for server-side filtering
Manager:
- addActions(array) → bulk registration
- getActions() → normalized and authorization-filtered array
Flat vs Tree
- Flat: use
parentandchildrenids to nest menus. - Tree: provide nested
childrenarrays; library supports both.
Testing
Pest and Testbench included:
composer config --no-plugins allow-plugins.pestphp/pest-plugin true
composer update
./vendor/bin/pest
Run a single test:
./vendor/bin/pest tests/ManagerTest.php
Static analysis, style and coverage (optional suggestions):
- phpstan:
vendor/bin/phpstan analyse src - pint:
vendor/bin/pint - coverage:
./vendor/bin/pest --coverage-html coverage/
Troubleshooting
- Handler not firing:
- Ensure
handleris a string and the global function exists:window.myFunction = (detail) => { ... }
- Ensure
- Action hidden unexpectedly:
- Check
can()abilities; Gate must return true for at least one ability.
- Check
- Material Icons missing:
- Ensure
material_icons_urlis set, or disable withnoAutoLoadMdIcons.
- Ensure
- Theme not switching on auto:
- Confirm browser supports
prefers-color-scheme; ensure no CSS overrides are preventing.darkclass effects.
- Confirm browser supports
- CDN blocked:
- Switch to local asset by setting
use_cdn=falseandasset_path.
- Switch to local asset by setting
Performance
- Avoid registering an excessive number of actions; use
keywordsfor better searchability. - Group actions logically via
sectionto improve UX. - Prefer short, deterministic handlers; delegate long work to async functions if needed.
- Cache computed action arrays on the server if you build them dynamically on each request.
FAQ
- Difference between
shortcutandhotkey?shortcutis normalized tohotkey. You can use either, but onlyhotkeyis sent to the component.
- Can I navigate externally?
- Store
hrefandtargeton actions and implement logic in your handler to perform navigation.
- Store
- How to hide breadcrumbs?
- Set
hideBreadcrumbs=truein config.
- Set
- How to disable registering action hotkeys?
- Set
disableHotkeys=truein config to stop auto-registering action hotkeys.
- Set
- Flat vs Tree data?
- Use
parent/childrenin flat mode or nested arrays in tree mode; pick whichever suits your data model.
- Use
CI & Publishing
- Validate and test:
composer validate ./vendor/bin/pest
- Tag a release (after publishing to Packagist):
- Follow SemVer; update composer version range if needed.
- GitHub Actions (suggested):
- Setup PHP matrix (^8.1), run
composer install,composer validate,./vendor/bin/pest.
- Setup PHP matrix (^8.1), run
Versioning
- Follows semantic versioning once published.
- Compatibility targets: PHP ^8.1, Laravel ^10|^11.
Contributing
- PRs and issues are welcome. Please include tests where appropriate.
Security
- No secrets stored; do not commit keys/tokens. Report vulnerabilities privately.
License
- MIT © AmjadIqbal
Credits
- Built on top of the excellent ninja-keys by Sergei Sleptsov.