@flxgde/gigamenu 0.2.0 → 0.5.0
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/README.md +304 -15
- package/fesm2022/flxgde-gigamenu.mjs +1103 -158
- package/fesm2022/flxgde-gigamenu.mjs.map +1 -1
- package/package.json +1 -1
- package/styles.css +2 -2
- package/types/flxgde-gigamenu.d.ts +175 -48
package/README.md
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# Gigamenu
|
|
2
2
|
|
|
3
|
-
A keyboard-driven command palette menu for Angular applications. Inspired by VS Code's Command Palette, Spotlight
|
|
3
|
+
A keyboard-driven command palette menu for Angular applications. Inspired by VS Code's Command Palette, Spotlight and zsh-completion.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- Keyboard shortcuts
|
|
8
|
-
- Auto-discovery
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
7
|
+
- **Keyboard shortcuts**: `Ctrl/Cmd+K` and `/` (when no input is focused)
|
|
8
|
+
- **Auto-discovery**: Routes from Angular Router with filtering and mapping
|
|
9
|
+
- **Command registration**: With parameters, keywords, and icon support
|
|
10
|
+
- **Step-by-step input**: Select action first, then fill parameters one at a time
|
|
11
|
+
- **Keyboard navigation**: Arrow keys, Enter, Escape, Tab for step progression
|
|
12
|
+
- **Zsh-like editing**: `Ctrl+W` delete word, `Ctrl+U` clear line, and more
|
|
13
|
+
- **Smart search**: Multi-word fuzzy filtering with keyword matching
|
|
14
|
+
- **Frecency ranking**: Learns from your usage patterns
|
|
15
|
+
- **Parameter autocomplete**: Suggestions shown in main list during parameter input
|
|
16
|
+
- **Breadcrumb navigation**: Visual trail of locked action + filled parameters
|
|
17
|
+
- **Custom templates**: 5 template directives for full UI customization
|
|
18
|
+
- **Dark mode**: Configurable class name support
|
|
19
|
+
- **Icon libraries**: Support for emoji and CSS icon classes (FontAwesome, PrimeIcons, etc.)
|
|
20
|
+
- **Type-safe**: Full TypeScript support with helper functions
|
|
21
|
+
- **Tailwind CSS**: Beautiful default styling with dark mode variants
|
|
14
22
|
|
|
15
23
|
## Installation
|
|
16
24
|
|
|
@@ -53,7 +61,7 @@ this.gigamenu.registerCommand({
|
|
|
53
61
|
description: 'Switch between light and dark theme',
|
|
54
62
|
icon: '🌙',
|
|
55
63
|
keywords: ['theme', 'dark', 'light'],
|
|
56
|
-
action: () =>
|
|
64
|
+
action: () => this.gigamenu.toggleDarkMode(),
|
|
57
65
|
});
|
|
58
66
|
```
|
|
59
67
|
|
|
@@ -68,16 +76,128 @@ this.gigamenu.registerPage({
|
|
|
68
76
|
});
|
|
69
77
|
```
|
|
70
78
|
|
|
79
|
+
### 4. Commands with Parameters
|
|
80
|
+
|
|
81
|
+
Commands can define parameters that are filled in step-by-step:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
this.gigamenu.registerCommand({
|
|
85
|
+
id: 'cmd:send-message',
|
|
86
|
+
label: 'Send Message',
|
|
87
|
+
description: 'Send a message to a user',
|
|
88
|
+
icon: '💬',
|
|
89
|
+
keywords: ['message', 'chat', 'notify'],
|
|
90
|
+
params: ['user', 'message'], // Parameters filled one at a time
|
|
91
|
+
paramProviders: {
|
|
92
|
+
user: [
|
|
93
|
+
{ label: 'Alice', value: 'user-1' },
|
|
94
|
+
{ label: 'Bob', value: 'user-2' },
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
action: (args) => {
|
|
98
|
+
// args contains space-separated parameter values
|
|
99
|
+
console.log('Sending:', args);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Step-by-step flow:**
|
|
105
|
+
1. User searches "Send Message" and presses Tab/Enter
|
|
106
|
+
2. Action is locked, input clears for "user" parameter
|
|
107
|
+
3. Autocomplete suggestions appear in the list
|
|
108
|
+
4. User selects or types a value, presses Tab
|
|
109
|
+
5. Input clears for "message" parameter
|
|
110
|
+
6. User types message, presses Enter to execute
|
|
111
|
+
|
|
112
|
+
### 5. Route Discovery with Filtering and Mapping
|
|
113
|
+
|
|
114
|
+
Customize which routes are discovered and how they appear:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
this.gigamenu.discoverRoutes({
|
|
118
|
+
filter: (route) => {
|
|
119
|
+
// Exclude admin routes
|
|
120
|
+
return !route.fullPath.includes('admin');
|
|
121
|
+
},
|
|
122
|
+
map: (route) => {
|
|
123
|
+
// Customize page data
|
|
124
|
+
return {
|
|
125
|
+
icon: route.data?.['icon'],
|
|
126
|
+
keywords: route.data?.['keywords'],
|
|
127
|
+
description: route.data?.['description'],
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 6. Custom Templates
|
|
134
|
+
|
|
135
|
+
Customize the appearance of menu items, empty states, header, footer, or the entire panel:
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<gm-gigamenu>
|
|
139
|
+
<!-- Custom item template -->
|
|
140
|
+
<ng-template gmItem let-item let-selected="selected">
|
|
141
|
+
<div [class.selected]="selected">
|
|
142
|
+
<span>{{ item.icon }}</span>
|
|
143
|
+
<strong>{{ item.label }}</strong>
|
|
144
|
+
<em>{{ item.description }}</em>
|
|
145
|
+
</div>
|
|
146
|
+
</ng-template>
|
|
147
|
+
|
|
148
|
+
<!-- Custom empty state -->
|
|
149
|
+
<ng-template gmEmpty let-query>
|
|
150
|
+
<p>No results for "{{ query }}"</p>
|
|
151
|
+
</ng-template>
|
|
152
|
+
|
|
153
|
+
<!-- Custom header with breadcrumb support -->
|
|
154
|
+
<ng-template gmHeader let-query
|
|
155
|
+
let-lockedAction="lockedAction"
|
|
156
|
+
let-paramValues="paramValues"
|
|
157
|
+
let-currentParamName="currentParamName"
|
|
158
|
+
let-onQueryChange="onQueryChange"
|
|
159
|
+
let-onKeydown="onKeydown"
|
|
160
|
+
let-onUnlockAction="onUnlockAction"
|
|
161
|
+
let-placeholder="placeholder">
|
|
162
|
+
<!-- Show breadcrumb when action is locked -->
|
|
163
|
+
@if (lockedAction) {
|
|
164
|
+
<div class="breadcrumb">
|
|
165
|
+
<button (click)="onUnlockAction()">{{ lockedAction.label }}</button>
|
|
166
|
+
@for (value of paramValues; track $index) {
|
|
167
|
+
<span>›</span>
|
|
168
|
+
<span>{{ lockedAction.params?.[$index] }}: {{ value }}</span>
|
|
169
|
+
}
|
|
170
|
+
@if (currentParamName) {
|
|
171
|
+
<span>› {{ currentParamName }}:</span>
|
|
172
|
+
}
|
|
173
|
+
</div>
|
|
174
|
+
}
|
|
175
|
+
<input
|
|
176
|
+
[value]="query"
|
|
177
|
+
[placeholder]="lockedAction ? currentParamName : placeholder"
|
|
178
|
+
(input)="onQueryChange($any($event.target).value)"
|
|
179
|
+
(keydown)="onKeydown($event)" />
|
|
180
|
+
</ng-template>
|
|
181
|
+
|
|
182
|
+
<!-- Custom footer -->
|
|
183
|
+
<ng-template gmFooter let-count let-total="total">
|
|
184
|
+
<p>Showing {{ count }} of {{ total }} items</p>
|
|
185
|
+
</ng-template>
|
|
186
|
+
</gm-gigamenu>
|
|
187
|
+
```
|
|
188
|
+
|
|
71
189
|
## API
|
|
72
190
|
|
|
73
191
|
### GigamenuService
|
|
74
192
|
|
|
75
193
|
| Method | Description |
|
|
76
194
|
|--------|-------------|
|
|
195
|
+
| `setRouter(router)` | Set the router instance (required for navigation) |
|
|
77
196
|
| `open()` | Open the menu |
|
|
78
197
|
| `close()` | Close the menu |
|
|
79
198
|
| `toggle()` | Toggle menu visibility |
|
|
80
|
-
| `
|
|
199
|
+
| `toggleDarkMode()` | Toggle dark mode using configured class |
|
|
200
|
+
| `discoverRoutes(options?)` | Auto-discover pages from Angular Router with optional filter/map |
|
|
81
201
|
| `registerCommand(command)` | Register a custom command |
|
|
82
202
|
| `registerPage(page)` | Register a custom page |
|
|
83
203
|
| `registerItem(item)` | Register a generic menu item |
|
|
@@ -91,6 +211,8 @@ interface GigamenuConfig {
|
|
|
91
211
|
placeholder?: string; // Search input placeholder
|
|
92
212
|
maxResults?: number; // Maximum items to show (default: 10)
|
|
93
213
|
autoDiscoverRoutes?: boolean; // Auto-discover on init
|
|
214
|
+
argSeparator?: string; // Separator between args when passed to action
|
|
215
|
+
darkModeClass?: string; // CSS class for dark mode (default: 'dark')
|
|
94
216
|
}
|
|
95
217
|
```
|
|
96
218
|
|
|
@@ -101,26 +223,193 @@ interface GigamenuItem {
|
|
|
101
223
|
id: string;
|
|
102
224
|
label: string;
|
|
103
225
|
description?: string;
|
|
226
|
+
icon?: string; // Emoji or text icon
|
|
227
|
+
iconClass?: string; // CSS class for icon libraries (e.g., 'pi pi-home', 'fa fa-user')
|
|
228
|
+
keywords?: string[]; // Additional searchable keywords
|
|
229
|
+
params?: string[]; // Required parameter names (e.g., ['id', 'commentId'])
|
|
230
|
+
category: 'page' | 'command';
|
|
231
|
+
action: (args?: string) => void; // Receives arguments after separator
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
interface GigamenuCommand extends Omit<GigamenuItem, 'category'> {
|
|
235
|
+
shortcut?: string; // Keyboard shortcut display (e.g., 'Ctrl+S')
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
interface GigamenuPage extends Omit<GigamenuItem, 'category' | 'action'> {
|
|
239
|
+
path: string; // Navigation path (params auto-extracted)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
interface DiscoverRoutesOptions {
|
|
243
|
+
filter?: (route: RouteInfo) => boolean; // Filter which routes to include
|
|
244
|
+
map?: (route: RouteInfo) => MappedPage | null; // Customize page data
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
interface RouteInfo {
|
|
248
|
+
path: string; // Segment path
|
|
249
|
+
fullPath: string; // Complete path from root
|
|
250
|
+
data?: Record<string, unknown>; // Route data
|
|
251
|
+
title?: string; // Route title
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
interface MappedPage {
|
|
255
|
+
label?: string;
|
|
256
|
+
description?: string;
|
|
104
257
|
icon?: string;
|
|
258
|
+
iconClass?: string;
|
|
105
259
|
keywords?: string[];
|
|
106
|
-
category: 'page' | 'command';
|
|
107
|
-
action: () => void;
|
|
108
260
|
}
|
|
261
|
+
|
|
262
|
+
// Type-safe command definitions
|
|
263
|
+
interface CommandDefinition {
|
|
264
|
+
readonly id: string;
|
|
265
|
+
readonly label: string;
|
|
266
|
+
readonly description?: string;
|
|
267
|
+
readonly icon?: string;
|
|
268
|
+
readonly iconClass?: string;
|
|
269
|
+
readonly keywords?: string[];
|
|
270
|
+
readonly shortcut?: string;
|
|
271
|
+
execute(): void;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Helper for type-safe command creation
|
|
275
|
+
function defineCommand(command: CommandDefinition): CommandDefinition
|
|
109
276
|
```
|
|
110
277
|
|
|
278
|
+
### Template Directives
|
|
279
|
+
|
|
280
|
+
All template directives provide context objects for customization:
|
|
281
|
+
|
|
282
|
+
| Directive | Purpose | Context Properties |
|
|
283
|
+
|-----------|---------|-------------------|
|
|
284
|
+
| `gmItem` | Custom item rendering | `item`, `index`, `selected` |
|
|
285
|
+
| `gmEmpty` | Empty state when no results | `query` (search term) |
|
|
286
|
+
| `gmHeader` | Search input and header area | `query`, `lockedAction`, `paramValues`, `currentParamName`, `placeholder`, `onQueryChange`, `onKeydown`, `onUnlockAction`, `onGoToParam` |
|
|
287
|
+
| `gmFooter` | Footer/status area | `count` (filtered), `total` (all items) |
|
|
288
|
+
| `gmPanel` | Entire panel container | `items`, `query`, `lockedAction`, `paramValues`, `selectedIndex`, `placeholder`, `onItemClick`, `onSelectIndex`, `onQueryChange`, `onClose` |
|
|
289
|
+
|
|
290
|
+
## Advanced Features
|
|
291
|
+
|
|
292
|
+
### Step-by-Step Input Flow
|
|
293
|
+
|
|
294
|
+
Gigamenu uses a step-by-step approach for commands with parameters:
|
|
295
|
+
|
|
296
|
+
**State Machine:**
|
|
297
|
+
1. **ActionSelection**: Search and select an action from the list
|
|
298
|
+
2. **ParameterInput**: Fill in parameters one at a time (when action has params)
|
|
299
|
+
|
|
300
|
+
**Behavior by Action Type:**
|
|
301
|
+
- **No params**: Tab or Enter executes immediately
|
|
302
|
+
- **Required params**: Tab or Enter locks action, enters parameter input mode
|
|
303
|
+
- **Optional params**: Enter executes, Tab enters parameter input mode
|
|
304
|
+
|
|
305
|
+
**Parameter Input Mode:**
|
|
306
|
+
- Input field only captures the current parameter value
|
|
307
|
+
- Main list shows autocomplete suggestions (if `paramProviders` defined)
|
|
308
|
+
- Breadcrumb shows: `[Action] › [param1: value] › [param2: value] › current:`
|
|
309
|
+
- Navigate suggestions with arrow keys, select with Tab or Enter
|
|
310
|
+
|
|
311
|
+
**Navigation:**
|
|
312
|
+
- Backspace (on empty) or Escape: Go back to previous parameter
|
|
313
|
+
- At first parameter: Go back to action selection
|
|
314
|
+
- Click breadcrumb items to jump back to any step
|
|
315
|
+
|
|
316
|
+
### Frecency-Based Ranking
|
|
317
|
+
|
|
318
|
+
Gigamenu uses intelligent ranking based on **frequency** and **recency** of selections:
|
|
319
|
+
|
|
320
|
+
- Learns from your usage patterns
|
|
321
|
+
- Recent selections are weighted higher
|
|
322
|
+
- Automatically selects frequently used items
|
|
323
|
+
- Decays scores over time (formula: `count × 0.9^ageInHours`)
|
|
324
|
+
- Persists to localStorage for cross-session learning
|
|
325
|
+
- Auto-prunes low-scoring entries (max 100 terms, 10 items per term)
|
|
326
|
+
|
|
327
|
+
No configuration needed - it works automatically!
|
|
328
|
+
|
|
329
|
+
### Parameter Color Coding
|
|
330
|
+
|
|
331
|
+
Filled parameter values in the breadcrumb are color-coded:
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
[Send Message] › [user: Alice] › [message: Hello]
|
|
335
|
+
↑ blue ↑ green
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
5 colors cycle for multiple parameters: blue, green, orange, pink, cyan (with dark mode variants).
|
|
339
|
+
|
|
111
340
|
## Keyboard Shortcuts
|
|
112
341
|
|
|
342
|
+
### Action Selection Mode
|
|
113
343
|
| Shortcut | Action |
|
|
114
344
|
|----------|--------|
|
|
115
|
-
| `Ctrl/Cmd+K` | Open menu |
|
|
345
|
+
| `Ctrl/Cmd+K` | Open/toggle menu |
|
|
116
346
|
| `/` | Open menu (when no input focused) |
|
|
117
347
|
| `↑` / `↓` | Navigate items |
|
|
118
|
-
| `Enter` | Execute
|
|
348
|
+
| `Enter` | Execute (or enter param mode if required params) |
|
|
349
|
+
| `Tab` | Enter param mode (or execute if no params) |
|
|
119
350
|
| `Escape` | Close menu |
|
|
120
351
|
|
|
352
|
+
### Parameter Input Mode
|
|
353
|
+
| Shortcut | Action |
|
|
354
|
+
|----------|--------|
|
|
355
|
+
| `↑` / `↓` | Navigate autocomplete suggestions |
|
|
356
|
+
| `Tab` | Accept suggestion + next param (or execute if last) |
|
|
357
|
+
| `Enter` | Accept suggestion + next param (or execute if last) |
|
|
358
|
+
| `Backspace` (empty) | Go back to previous param |
|
|
359
|
+
| `Escape` | Go back to previous param (or action selection) |
|
|
360
|
+
|
|
361
|
+
### Zsh-like Editing
|
|
362
|
+
| Shortcut | Action |
|
|
363
|
+
|----------|--------|
|
|
364
|
+
| `Ctrl+W` | Delete last word |
|
|
365
|
+
| `Ctrl+U` | Clear entire line |
|
|
366
|
+
| `Ctrl+Backspace` | Delete last word |
|
|
367
|
+
| `Alt+Backspace` | Delete last word |
|
|
368
|
+
|
|
121
369
|
## Styling
|
|
122
370
|
|
|
123
|
-
Gigamenu uses Tailwind CSS and supports dark mode
|
|
371
|
+
Gigamenu uses Tailwind CSS and supports dark mode. By default, it uses the `dark` class on `<html>`, but this is customizable via the `darkModeClass` configuration option:
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
this.gigamenu.configure({
|
|
375
|
+
darkModeClass: 'dark-theme', // Use your custom class name
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Architecture
|
|
380
|
+
|
|
381
|
+
For contributors and maintainers, here's an overview of the codebase structure:
|
|
382
|
+
|
|
383
|
+
### File Structure
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
src/lib/
|
|
387
|
+
├── gigamenu.component.ts # Main component (~520 lines, orchestrator)
|
|
388
|
+
├── gigamenu.component.html # Template
|
|
389
|
+
├── gigamenu.service.ts # State management service
|
|
390
|
+
├── frecency.service.ts # Usage-based ranking
|
|
391
|
+
├── types.ts # Type definitions
|
|
392
|
+
├── query-parser.ts # Query/args parsing
|
|
393
|
+
├── input-state.ts # State machine enum
|
|
394
|
+
├── gigamenu-templates.directive.ts # Template directives
|
|
395
|
+
└── core/ # Pure functions (business logic)
|
|
396
|
+
├── index.ts # Barrel export
|
|
397
|
+
├── scroll-utils.ts # DOM scroll helpers
|
|
398
|
+
├── search.ts # Filtering & sorting
|
|
399
|
+
├── parameter-state.ts # Parameter computations
|
|
400
|
+
├── template-contexts.ts # Template context builders
|
|
401
|
+
├── menu-lifecycle.ts # Menu state utilities
|
|
402
|
+
├── autocomplete.ts # Autocomplete logic
|
|
403
|
+
└── keyboard-handlers.ts # Keyboard handlers + actions
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Design Principles
|
|
407
|
+
|
|
408
|
+
- **Separation of concerns**: Business logic lives in `core/` as pure, testable functions
|
|
409
|
+
- **Action dispatch pattern**: Keyboard handlers return `MenuAction[]` objects instead of mutating state
|
|
410
|
+
- **State machine**: `InputState` enum (Closed, ActionSelection, ParameterInput) drives behavior
|
|
411
|
+
- **Step-by-step flow**: Input parses one thing at a time (action search OR parameter value)
|
|
412
|
+
- **Angular signals**: All reactive state uses `signal()` and `computed()`
|
|
124
413
|
|
|
125
414
|
## License
|
|
126
415
|
|