@scania-nl/tegel-angular-extensions 0.0.6 → 0.0.7

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 CHANGED
@@ -21,7 +21,8 @@ Provides simple wrappers for toast and modal (TBC) functionality using Angular 1
21
21
  - Drop-in UI integrations for Tegel Angular
22
22
  - Signal-based `ToastService` for displaying toasts
23
23
  - Customizable toast appearance and behavior
24
- 3. **Default Nginx config**
24
+ 3. **Standalone components and directives**
25
+ 4. **Default Nginx config**
25
26
 
26
27
  - Zero boilerplate - no Angular modules required
27
28
  - Fully typed and configurable via DI
@@ -56,6 +57,7 @@ Provides simple wrappers for toast and modal (TBC) functionality using Angular 1
56
57
  1. [Footer](#footer-component-tae-footer)
57
58
  2. [Directives](#-directives)
58
59
  1. [Hard Refresh](#hard-refresh-directive-taehardrefresh)
60
+ 2. [Barcode Scanner](#barcode-scanner-directive-taebarcodescanner)
59
61
  5. [Appendix](#appendix)
60
62
  1. [Dockerfile Example](#runtime-config-dockerfile-example)
61
63
  6. [License](#-license)
@@ -488,10 +490,10 @@ This is especially useful for hard-reloading tablet or mobile applications which
488
490
 
489
491
  **Inputs:**
490
492
 
491
- | Input | Type | Default | Description |
492
- | -------------- | ------ | ------- | ---------------------------------------------------------------------- |
493
- | clicksRequired | number | 3 | Number of clicks required within the window to trigger a hard refresh. |
494
- | clickWindowMs | number | 500 | Time window in milliseconds between two subsequent clicks. |
493
+ | Property | Type | Default | Description |
494
+ | ---------------- | -------- | --------- | ---------------------------------------------------------------------- |
495
+ | `clicksRequired` | `number` | `3` | Number of clicks required within the window to trigger a hard refresh. |
496
+ | `clickWindowMs` | `number` | `500` | Time window in milliseconds between two subsequent clicks. |
495
497
 
496
498
  **Example:**
497
499
 
@@ -505,6 +507,40 @@ This is especially useful for hard-reloading tablet or mobile applications which
505
507
  </tds-header-brand-symbol>
506
508
  ```
507
509
 
510
+ &nbsp;
511
+
512
+ ### Barcode Scanner Directive (`taeBarcodeScanner`)
513
+
514
+ The `BarcodeScannerDirective` enables global barcode scanning by listening for rapid character input sequences (typically from a hardware scanner) that terminate with a specific key (default is `Enter`).
515
+
516
+ It operates outside of Angular's zone to prevent unnecessary change detection cycles on every keypress and includes built-in filtering to ignore modifier keys like `Shift`, `Ctrl`, or `Alt`, ensuring only the actual barcode data is captured.
517
+
518
+ **Inputs:**
519
+
520
+ | Input | Type | Default | Description |
521
+ | ---------------- | ---------------- | --------- | ------------------------------------------------------------------ |
522
+ | `inputWindowMs` | `number` | `100` | Max time allowed between consecutive keypresses before discarding. |
523
+ | `terminatorKey` | `string` | `'Enter'` | The key that signals the end of a barcode sequence. |
524
+
525
+ **Outputs:**
526
+
527
+ | Output | Type | Description |
528
+ | ---------------- | ---------------- | ------------------------------------------------------------------ |
529
+ | `barcodeScanned` | `string` | **Required**. Emits the full barcode string once completed. |
530
+
531
+ **Example:**
532
+
533
+ ```html
534
+ <ng-container
535
+ taeBarcodeScanner
536
+ (barcodeScanned)="handleScan($event)"
537
+ [inputWindowMs]="100"
538
+ terminatorKey="Enter"
539
+ >
540
+ <p>Scanner is active. Please scan a barcode...</p>
541
+ </ng-container>
542
+ ```
543
+
508
544
  ---
509
545
 
510
546
  &nbsp;
package/esm2022/index.mjs CHANGED
@@ -16,9 +16,10 @@ export { provideStaticConfig } from './lib/env/angular/provide-static-config';
16
16
  // --------------------------------------------------
17
17
  // Directive Module Exports
18
18
  // --------------------------------------------------
19
+ export { BarcodeScannerDirective } from './lib/directives/barcode-scanner.directive';
19
20
  export { HardRefreshDirective } from './lib/directives/hard-refresh.directive';
20
21
  // --------------------------------------------------
21
22
  // Component Module Exports
22
23
  // --------------------------------------------------
23
24
  export { TaeFooterComponent } from './lib/components/tae-footer/tae-footer.component';
24
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWJzL3RlZ2VsLWFuZ3VsYXItZXh0ZW5zaW9ucy9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEscURBQXFEO0FBQ3JELHVCQUF1QjtBQUN2QixxREFBcUQ7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsWUFBWSxHQUViLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBS3pELHFEQUFxRDtBQUNyRCxxQkFBcUI7QUFDckIscURBQXFEO0FBQ3JELE9BQU87QUFDUCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTdELFVBQVU7QUFDVixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUNoRixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUU5RSxxREFBcUQ7QUFDckQsMkJBQTJCO0FBQzNCLHFEQUFxRDtBQUNyRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUUvRSxxREFBcUQ7QUFDckQsMkJBQTJCO0FBQzNCLHFEQUFxRDtBQUNyRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxrREFBa0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBUb2FzdCBNb2R1bGUgRXhwb3J0c1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbmV4cG9ydCB7IHByb3ZpZGVUb2FzdCB9IGZyb20gJy4vbGliL3RvYXN0L3Byb3ZpZGUtdG9hc3QnO1xuZXhwb3J0IHtcbiAgREVGQVVMVF9UT0FTVF9DT05GSUcsXG4gIFRPQVNUX0NPTkZJRyxcbiAgVG9hc3RDb25maWcsXG59IGZyb20gJy4vbGliL3RvYXN0L3RvYXN0LmNvbmZpZyc7XG5leHBvcnQgeyBUb2FzdFNlcnZpY2UgfSBmcm9tICcuL2xpYi90b2FzdC90b2FzdC5zZXJ2aWNlJztcblxuLy8gTW9kZWxzXG5leHBvcnQgeyBUb2FzdCwgVG9hc3RPcHRpb25zIH0gZnJvbSAnLi9saWIvdG9hc3QvbW9kZWxzL3RvYXN0Lm1vZGVsJztcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEVudiBNb2R1bGUgRXhwb3J0c1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIENvcmVcbmV4cG9ydCB7IGNyZWF0ZUVudktpdCB9IGZyb20gJy4vbGliL2Vudi9jb3JlL2NyZWF0ZS1lbnYta2l0JztcbmV4cG9ydCB7IHBhcnNlRW52RmlsZSB9IGZyb20gJy4vbGliL2Vudi9jb3JlL3BhcnNlLWVudi1maWxlJztcblxuLy8gQW5ndWxhclxuZXhwb3J0IHsgcHJvdmlkZVJ1bnRpbWVDb25maWcgfSBmcm9tICcuL2xpYi9lbnYvYW5ndWxhci9wcm92aWRlLXJ1bnRpbWUtY29uZmlnJztcbmV4cG9ydCB7IHByb3ZpZGVTdGF0aWNDb25maWcgfSBmcm9tICcuL2xpYi9lbnYvYW5ndWxhci9wcm92aWRlLXN0YXRpYy1jb25maWcnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gRGlyZWN0aXZlIE1vZHVsZSBFeHBvcnRzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0IHsgSGFyZFJlZnJlc2hEaXJlY3RpdmUgfSBmcm9tICcuL2xpYi9kaXJlY3RpdmVzL2hhcmQtcmVmcmVzaC5kaXJlY3RpdmUnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gQ29tcG9uZW50IE1vZHVsZSBFeHBvcnRzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0IHsgVGFlRm9vdGVyQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy90YWUtZm9vdGVyL3RhZS1mb290ZXIuY29tcG9uZW50JztcbiJdfQ==
25
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWJzL3RlZ2VsLWFuZ3VsYXItZXh0ZW5zaW9ucy9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEscURBQXFEO0FBQ3JELHVCQUF1QjtBQUN2QixxREFBcUQ7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsWUFBWSxHQUViLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBS3pELHFEQUFxRDtBQUNyRCxxQkFBcUI7QUFDckIscURBQXFEO0FBQ3JELE9BQU87QUFDUCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTdELFVBQVU7QUFDVixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUNoRixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUU5RSxxREFBcUQ7QUFDckQsMkJBQTJCO0FBQzNCLHFEQUFxRDtBQUNyRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUNyRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUUvRSxxREFBcUQ7QUFDckQsMkJBQTJCO0FBQzNCLHFEQUFxRDtBQUNyRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxrREFBa0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBUb2FzdCBNb2R1bGUgRXhwb3J0c1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbmV4cG9ydCB7IHByb3ZpZGVUb2FzdCB9IGZyb20gJy4vbGliL3RvYXN0L3Byb3ZpZGUtdG9hc3QnO1xuZXhwb3J0IHtcbiAgREVGQVVMVF9UT0FTVF9DT05GSUcsXG4gIFRPQVNUX0NPTkZJRyxcbiAgVG9hc3RDb25maWcsXG59IGZyb20gJy4vbGliL3RvYXN0L3RvYXN0LmNvbmZpZyc7XG5leHBvcnQgeyBUb2FzdFNlcnZpY2UgfSBmcm9tICcuL2xpYi90b2FzdC90b2FzdC5zZXJ2aWNlJztcblxuLy8gTW9kZWxzXG5leHBvcnQgeyBUb2FzdCwgVG9hc3RPcHRpb25zIH0gZnJvbSAnLi9saWIvdG9hc3QvbW9kZWxzL3RvYXN0Lm1vZGVsJztcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEVudiBNb2R1bGUgRXhwb3J0c1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIENvcmVcbmV4cG9ydCB7IGNyZWF0ZUVudktpdCB9IGZyb20gJy4vbGliL2Vudi9jb3JlL2NyZWF0ZS1lbnYta2l0JztcbmV4cG9ydCB7IHBhcnNlRW52RmlsZSB9IGZyb20gJy4vbGliL2Vudi9jb3JlL3BhcnNlLWVudi1maWxlJztcblxuLy8gQW5ndWxhclxuZXhwb3J0IHsgcHJvdmlkZVJ1bnRpbWVDb25maWcgfSBmcm9tICcuL2xpYi9lbnYvYW5ndWxhci9wcm92aWRlLXJ1bnRpbWUtY29uZmlnJztcbmV4cG9ydCB7IHByb3ZpZGVTdGF0aWNDb25maWcgfSBmcm9tICcuL2xpYi9lbnYvYW5ndWxhci9wcm92aWRlLXN0YXRpYy1jb25maWcnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gRGlyZWN0aXZlIE1vZHVsZSBFeHBvcnRzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0IHsgQmFyY29kZVNjYW5uZXJEaXJlY3RpdmUgfSBmcm9tICcuL2xpYi9kaXJlY3RpdmVzL2JhcmNvZGUtc2Nhbm5lci5kaXJlY3RpdmUnO1xuZXhwb3J0IHsgSGFyZFJlZnJlc2hEaXJlY3RpdmUgfSBmcm9tICcuL2xpYi9kaXJlY3RpdmVzL2hhcmQtcmVmcmVzaC5kaXJlY3RpdmUnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gQ29tcG9uZW50IE1vZHVsZSBFeHBvcnRzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0IHsgVGFlRm9vdGVyQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy90YWUtZm9vdGVyL3RhZS1mb290ZXIuY29tcG9uZW50JztcbiJdfQ==
@@ -0,0 +1,94 @@
1
+ import { DOCUMENT } from '@angular/common';
2
+ import { Directive, inject, input, NgZone, output } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { catchError, EMPTY, exhaustMap, filter, fromEvent, map, share, startWith, takeWhile, timeout, toArray, } from 'rxjs';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * Directive that listens for rapid key sequences ending in a terminator key
8
+ * (default: Enter) and emits the collected characters as a barcode string.
9
+ *
10
+ * @example
11
+ * <ng-container
12
+ * taeBarcodeScanner
13
+ * (barcodeScanned)="handleScan($event)"
14
+ * [inputWindowMs]="100"
15
+ * terminatorKey="Enter"
16
+ * />
17
+ */
18
+ export class BarcodeScannerDirective {
19
+ /**
20
+ * Angular zone used to run key handling outside of change detection.
21
+ */
22
+ _ngZone = inject(NgZone);
23
+ /**
24
+ * Document reference for attaching global keydown event listeners.
25
+ */
26
+ _document = inject(DOCUMENT);
27
+ /**
28
+ * Maximum time window in milliseconds between consecutive keypresses.
29
+ * Sequences exceeding this duration are discarded.
30
+ *
31
+ * @default 100
32
+ */
33
+ inputWindowMs = input(100);
34
+ /**
35
+ * Key that terminates and completes a barcode sequence.
36
+ *
37
+ * @default "Enter"
38
+ */
39
+ terminatorKey = input('Enter');
40
+ /**
41
+ * Emits the complete barcode string captured from the scanner.
42
+ * * @remarks
43
+ * This output is required by the directive selector.
44
+ */
45
+ barcodeScanned = output();
46
+ /**
47
+ * Sets up the global keydown listener and barcode detection pipeline.
48
+ * The pipeline runs outside Angular's zone and only re-enters when a barcode
49
+ * is emitted, to avoid triggering change detection on every keypress.
50
+ */
51
+ constructor() {
52
+ // Run the key handling pipeline outside Angular's zone to avoid triggering
53
+ // change detection on every keypress.
54
+ this._ngZone.runOutsideAngular(() => {
55
+ // Create a global stream of valid keydown events
56
+ const key$ = fromEvent(this._document, 'keydown').pipe(
57
+ // Auto-unsubscribe when the directive is destroyed.
58
+ takeUntilDestroyed(),
59
+ // Allow only character keys and the terminator key
60
+ // Modifier keys (Shift, Ctrl, Alt, Meta) are ignored
61
+ filter((e) => e.key.length === 1 || e.key === this.terminatorKey()),
62
+ // Share a single keydown stream between outer and inner subscribers.
63
+ share());
64
+ key$
65
+ .pipe(
66
+ // Start a new scan session on the first key event
67
+ exhaustMap((firstEvent) => key$.pipe(startWith(firstEvent),
68
+ // Collect until the terminator key is pressed
69
+ takeWhile((e) => e.key !== this.terminatorKey()),
70
+ // Abort the sequence if no key is pressed within the time window
71
+ timeout(this.inputWindowMs()),
72
+ // Materialize the sequence as an array of KeyboardEvents
73
+ toArray(),
74
+ // Ignore empty sequences (e.g. stray terminator presses)
75
+ filter((events) => events.length > 0),
76
+ // On timeout (or other error), drop the sequence silently
77
+ catchError(() => EMPTY))),
78
+ // Map collected key events to their key values and join into a string
79
+ map((events) => events.map((e) => e.key).join('')))
80
+ // Re-enter Angular's zone only to emit the completed barcode.
81
+ .subscribe((barcode) => this._ngZone.run(() => this.barcodeScanned.emit(barcode)));
82
+ });
83
+ }
84
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BarcodeScannerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
85
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.17", type: BarcodeScannerDirective, isStandalone: true, selector: "[taeBarcodeScanner]", inputs: { inputWindowMs: { classPropertyName: "inputWindowMs", publicName: "inputWindowMs", isSignal: true, isRequired: false, transformFunction: null }, terminatorKey: { classPropertyName: "terminatorKey", publicName: "terminatorKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { barcodeScanned: "barcodeScanned" }, ngImport: i0 });
86
+ }
87
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BarcodeScannerDirective, decorators: [{
88
+ type: Directive,
89
+ args: [{
90
+ selector: '[taeBarcodeScanner]',
91
+ standalone: true,
92
+ }]
93
+ }], ctorParameters: () => [] });
94
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFyY29kZS1zY2FubmVyLmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvdGVnZWwtYW5ndWxhci1leHRlbnNpb25zL3NyYy9saWIvZGlyZWN0aXZlcy9iYXJjb2RlLXNjYW5uZXIuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzQyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNoRSxPQUFPLEVBQ0wsVUFBVSxFQUNWLEtBQUssRUFDTCxVQUFVLEVBQ1YsTUFBTSxFQUNOLFNBQVMsRUFDVCxHQUFHLEVBQ0gsS0FBSyxFQUNMLFNBQVMsRUFDVCxTQUFTLEVBQ1QsT0FBTyxFQUNQLE9BQU8sR0FDUixNQUFNLE1BQU0sQ0FBQzs7QUFFZDs7Ozs7Ozs7Ozs7R0FXRztBQUtILE1BQU0sT0FBTyx1QkFBdUI7SUFDbEM7O09BRUc7SUFDYyxPQUFPLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRTFDOztPQUVHO0lBQ2MsU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUU5Qzs7Ozs7T0FLRztJQUNNLGFBQWEsR0FBRyxLQUFLLENBQVMsR0FBRyxDQUFDLENBQUM7SUFFNUM7Ozs7T0FJRztJQUNNLGFBQWEsR0FBRyxLQUFLLENBQVMsT0FBTyxDQUFDLENBQUM7SUFFaEQ7Ozs7T0FJRztJQUNNLGNBQWMsR0FBRyxNQUFNLEVBQVUsQ0FBQztJQUUzQzs7OztPQUlHO0lBQ0g7UUFDRSwyRUFBMkU7UUFDM0Usc0NBQXNDO1FBQ3RDLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLENBQUMsR0FBRyxFQUFFO1lBQ2xDLGlEQUFpRDtZQUNqRCxNQUFNLElBQUksR0FBRyxTQUFTLENBQWdCLElBQUksQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQUMsSUFBSTtZQUNuRSxvREFBb0Q7WUFDcEQsa0JBQWtCLEVBQUU7WUFDcEIsbURBQW1EO1lBQ25ELHFEQUFxRDtZQUNyRCxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxLQUFLLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUNuRSxxRUFBcUU7WUFDckUsS0FBSyxFQUFFLENBQ1IsQ0FBQztZQUVGLElBQUk7aUJBQ0QsSUFBSTtZQUNILGtEQUFrRDtZQUNsRCxVQUFVLENBQUMsQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUN4QixJQUFJLENBQUMsSUFBSSxDQUNQLFNBQVMsQ0FBQyxVQUFVLENBQUM7WUFDckIsOENBQThDO1lBQzlDLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsS0FBSyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDaEQsaUVBQWlFO1lBQ2pFLE9BQU8sQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDN0IseURBQXlEO1lBQ3pELE9BQU8sRUFBRTtZQUNULHlEQUF5RDtZQUN6RCxNQUFNLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1lBQ3JDLDBEQUEwRDtZQUMxRCxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQ3hCLENBQ0Y7WUFDRCxzRUFBc0U7WUFDdEUsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQ25EO2dCQUNELDhEQUE4RDtpQkFDN0QsU0FBUyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FDckIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FDMUQsQ0FBQztRQUNOLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQzt3R0EvRVUsdUJBQXVCOzRGQUF2Qix1QkFBdUI7OzRGQUF2Qix1QkFBdUI7a0JBSm5DLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLHFCQUFxQjtvQkFDL0IsVUFBVSxFQUFFLElBQUk7aUJBQ2pCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRE9DVU1FTlQgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgRGlyZWN0aXZlLCBpbmplY3QsIGlucHV0LCBOZ1pvbmUsIG91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgdGFrZVVudGlsRGVzdHJveWVkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZS9yeGpzLWludGVyb3AnO1xuaW1wb3J0IHtcbiAgY2F0Y2hFcnJvcixcbiAgRU1QVFksXG4gIGV4aGF1c3RNYXAsXG4gIGZpbHRlcixcbiAgZnJvbUV2ZW50LFxuICBtYXAsXG4gIHNoYXJlLFxuICBzdGFydFdpdGgsXG4gIHRha2VXaGlsZSxcbiAgdGltZW91dCxcbiAgdG9BcnJheSxcbn0gZnJvbSAncnhqcyc7XG5cbi8qKlxuICogRGlyZWN0aXZlIHRoYXQgbGlzdGVucyBmb3IgcmFwaWQga2V5IHNlcXVlbmNlcyBlbmRpbmcgaW4gYSB0ZXJtaW5hdG9yIGtleVxuICogKGRlZmF1bHQ6IEVudGVyKSBhbmQgZW1pdHMgdGhlIGNvbGxlY3RlZCBjaGFyYWN0ZXJzIGFzIGEgYmFyY29kZSBzdHJpbmcuXG4gKlxuICogQGV4YW1wbGVcbiAqIDxuZy1jb250YWluZXJcbiAqICAgdGFlQmFyY29kZVNjYW5uZXJcbiAqICAgKGJhcmNvZGVTY2FubmVkKT1cImhhbmRsZVNjYW4oJGV2ZW50KVwiXG4gKiAgIFtpbnB1dFdpbmRvd01zXT1cIjEwMFwiXG4gKiAgIHRlcm1pbmF0b3JLZXk9XCJFbnRlclwiXG4gKiAvPlxuICovXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICdbdGFlQmFyY29kZVNjYW5uZXJdJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbn0pXG5leHBvcnQgY2xhc3MgQmFyY29kZVNjYW5uZXJEaXJlY3RpdmUge1xuICAvKipcbiAgICogQW5ndWxhciB6b25lIHVzZWQgdG8gcnVuIGtleSBoYW5kbGluZyBvdXRzaWRlIG9mIGNoYW5nZSBkZXRlY3Rpb24uXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9uZ1pvbmUgPSBpbmplY3QoTmdab25lKTtcblxuICAvKipcbiAgICogRG9jdW1lbnQgcmVmZXJlbmNlIGZvciBhdHRhY2hpbmcgZ2xvYmFsIGtleWRvd24gZXZlbnQgbGlzdGVuZXJzLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBfZG9jdW1lbnQgPSBpbmplY3QoRE9DVU1FTlQpO1xuXG4gIC8qKlxuICAgKiBNYXhpbXVtIHRpbWUgd2luZG93IGluIG1pbGxpc2Vjb25kcyBiZXR3ZWVuIGNvbnNlY3V0aXZlIGtleXByZXNzZXMuXG4gICAqIFNlcXVlbmNlcyBleGNlZWRpbmcgdGhpcyBkdXJhdGlvbiBhcmUgZGlzY2FyZGVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCAxMDBcbiAgICovXG4gIHJlYWRvbmx5IGlucHV0V2luZG93TXMgPSBpbnB1dDxudW1iZXI+KDEwMCk7XG5cbiAgLyoqXG4gICAqIEtleSB0aGF0IHRlcm1pbmF0ZXMgYW5kIGNvbXBsZXRlcyBhIGJhcmNvZGUgc2VxdWVuY2UuXG4gICAqXG4gICAqIEBkZWZhdWx0IFwiRW50ZXJcIlxuICAgKi9cbiAgcmVhZG9ubHkgdGVybWluYXRvcktleSA9IGlucHV0PHN0cmluZz4oJ0VudGVyJyk7XG5cbiAgLyoqXG4gICAqIEVtaXRzIHRoZSBjb21wbGV0ZSBiYXJjb2RlIHN0cmluZyBjYXB0dXJlZCBmcm9tIHRoZSBzY2FubmVyLlxuICAgKiAqIEByZW1hcmtzXG4gICAqIFRoaXMgb3V0cHV0IGlzIHJlcXVpcmVkIGJ5IHRoZSBkaXJlY3RpdmUgc2VsZWN0b3IuXG4gICAqL1xuICByZWFkb25seSBiYXJjb2RlU2Nhbm5lZCA9IG91dHB1dDxzdHJpbmc+KCk7XG5cbiAgLyoqXG4gICAqIFNldHMgdXAgdGhlIGdsb2JhbCBrZXlkb3duIGxpc3RlbmVyIGFuZCBiYXJjb2RlIGRldGVjdGlvbiBwaXBlbGluZS5cbiAgICogVGhlIHBpcGVsaW5lIHJ1bnMgb3V0c2lkZSBBbmd1bGFyJ3Mgem9uZSBhbmQgb25seSByZS1lbnRlcnMgd2hlbiBhIGJhcmNvZGVcbiAgICogaXMgZW1pdHRlZCwgdG8gYXZvaWQgdHJpZ2dlcmluZyBjaGFuZ2UgZGV0ZWN0aW9uIG9uIGV2ZXJ5IGtleXByZXNzLlxuICAgKi9cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgLy8gUnVuIHRoZSBrZXkgaGFuZGxpbmcgcGlwZWxpbmUgb3V0c2lkZSBBbmd1bGFyJ3Mgem9uZSB0byBhdm9pZCB0cmlnZ2VyaW5nXG4gICAgLy8gY2hhbmdlIGRldGVjdGlvbiBvbiBldmVyeSBrZXlwcmVzcy5cbiAgICB0aGlzLl9uZ1pvbmUucnVuT3V0c2lkZUFuZ3VsYXIoKCkgPT4ge1xuICAgICAgLy8gQ3JlYXRlIGEgZ2xvYmFsIHN0cmVhbSBvZiB2YWxpZCBrZXlkb3duIGV2ZW50c1xuICAgICAgY29uc3Qga2V5JCA9IGZyb21FdmVudDxLZXlib2FyZEV2ZW50Pih0aGlzLl9kb2N1bWVudCwgJ2tleWRvd24nKS5waXBlKFxuICAgICAgICAvLyBBdXRvLXVuc3Vic2NyaWJlIHdoZW4gdGhlIGRpcmVjdGl2ZSBpcyBkZXN0cm95ZWQuXG4gICAgICAgIHRha2VVbnRpbERlc3Ryb3llZCgpLFxuICAgICAgICAvLyBBbGxvdyBvbmx5IGNoYXJhY3RlciBrZXlzIGFuZCB0aGUgdGVybWluYXRvciBrZXlcbiAgICAgICAgLy8gTW9kaWZpZXIga2V5cyAoU2hpZnQsIEN0cmwsIEFsdCwgTWV0YSkgYXJlIGlnbm9yZWRcbiAgICAgICAgZmlsdGVyKChlKSA9PiBlLmtleS5sZW5ndGggPT09IDEgfHwgZS5rZXkgPT09IHRoaXMudGVybWluYXRvcktleSgpKSxcbiAgICAgICAgLy8gU2hhcmUgYSBzaW5nbGUga2V5ZG93biBzdHJlYW0gYmV0d2VlbiBvdXRlciBhbmQgaW5uZXIgc3Vic2NyaWJlcnMuXG4gICAgICAgIHNoYXJlKCksXG4gICAgICApO1xuXG4gICAgICBrZXkkXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIC8vIFN0YXJ0IGEgbmV3IHNjYW4gc2Vzc2lvbiBvbiB0aGUgZmlyc3Qga2V5IGV2ZW50XG4gICAgICAgICAgZXhoYXVzdE1hcCgoZmlyc3RFdmVudCkgPT5cbiAgICAgICAgICAgIGtleSQucGlwZShcbiAgICAgICAgICAgICAgc3RhcnRXaXRoKGZpcnN0RXZlbnQpLFxuICAgICAgICAgICAgICAvLyBDb2xsZWN0IHVudGlsIHRoZSB0ZXJtaW5hdG9yIGtleSBpcyBwcmVzc2VkXG4gICAgICAgICAgICAgIHRha2VXaGlsZSgoZSkgPT4gZS5rZXkgIT09IHRoaXMudGVybWluYXRvcktleSgpKSxcbiAgICAgICAgICAgICAgLy8gQWJvcnQgdGhlIHNlcXVlbmNlIGlmIG5vIGtleSBpcyBwcmVzc2VkIHdpdGhpbiB0aGUgdGltZSB3aW5kb3dcbiAgICAgICAgICAgICAgdGltZW91dCh0aGlzLmlucHV0V2luZG93TXMoKSksXG4gICAgICAgICAgICAgIC8vIE1hdGVyaWFsaXplIHRoZSBzZXF1ZW5jZSBhcyBhbiBhcnJheSBvZiBLZXlib2FyZEV2ZW50c1xuICAgICAgICAgICAgICB0b0FycmF5KCksXG4gICAgICAgICAgICAgIC8vIElnbm9yZSBlbXB0eSBzZXF1ZW5jZXMgKGUuZy4gc3RyYXkgdGVybWluYXRvciBwcmVzc2VzKVxuICAgICAgICAgICAgICBmaWx0ZXIoKGV2ZW50cykgPT4gZXZlbnRzLmxlbmd0aCA+IDApLFxuICAgICAgICAgICAgICAvLyBPbiB0aW1lb3V0IChvciBvdGhlciBlcnJvciksIGRyb3AgdGhlIHNlcXVlbmNlIHNpbGVudGx5XG4gICAgICAgICAgICAgIGNhdGNoRXJyb3IoKCkgPT4gRU1QVFkpLFxuICAgICAgICAgICAgKSxcbiAgICAgICAgICApLFxuICAgICAgICAgIC8vIE1hcCBjb2xsZWN0ZWQga2V5IGV2ZW50cyB0byB0aGVpciBrZXkgdmFsdWVzIGFuZCBqb2luIGludG8gYSBzdHJpbmdcbiAgICAgICAgICBtYXAoKGV2ZW50cykgPT4gZXZlbnRzLm1hcCgoZSkgPT4gZS5rZXkpLmpvaW4oJycpKSxcbiAgICAgICAgKVxuICAgICAgICAvLyBSZS1lbnRlciBBbmd1bGFyJ3Mgem9uZSBvbmx5IHRvIGVtaXQgdGhlIGNvbXBsZXRlZCBiYXJjb2RlLlxuICAgICAgICAuc3Vic2NyaWJlKChiYXJjb2RlKSA9PlxuICAgICAgICAgIHRoaXMuX25nWm9uZS5ydW4oKCkgPT4gdGhpcy5iYXJjb2RlU2Nhbm5lZC5lbWl0KGJhcmNvZGUpKSxcbiAgICAgICAgKTtcbiAgICB9KTtcbiAgfVxufVxuIl19
package/index.d.ts CHANGED
@@ -464,6 +464,56 @@ type ProvideStaticConfigOptions<T> = {
464
464
  */
465
465
  declare function provideStaticConfig<T>(env: T | undefined, options: ProvideStaticConfigOptions<T>): EnvironmentProviders;
466
466
 
467
+ /**
468
+ * Directive that listens for rapid key sequences ending in a terminator key
469
+ * (default: Enter) and emits the collected characters as a barcode string.
470
+ *
471
+ * @example
472
+ * <ng-container
473
+ * taeBarcodeScanner
474
+ * (barcodeScanned)="handleScan($event)"
475
+ * [inputWindowMs]="100"
476
+ * terminatorKey="Enter"
477
+ * />
478
+ */
479
+ declare class BarcodeScannerDirective {
480
+ /**
481
+ * Angular zone used to run key handling outside of change detection.
482
+ */
483
+ private readonly _ngZone;
484
+ /**
485
+ * Document reference for attaching global keydown event listeners.
486
+ */
487
+ private readonly _document;
488
+ /**
489
+ * Maximum time window in milliseconds between consecutive keypresses.
490
+ * Sequences exceeding this duration are discarded.
491
+ *
492
+ * @default 100
493
+ */
494
+ readonly inputWindowMs: _angular_core.InputSignal<number>;
495
+ /**
496
+ * Key that terminates and completes a barcode sequence.
497
+ *
498
+ * @default "Enter"
499
+ */
500
+ readonly terminatorKey: _angular_core.InputSignal<string>;
501
+ /**
502
+ * Emits the complete barcode string captured from the scanner.
503
+ * * @remarks
504
+ * This output is required by the directive selector.
505
+ */
506
+ readonly barcodeScanned: _angular_core.OutputEmitterRef<string>;
507
+ /**
508
+ * Sets up the global keydown listener and barcode detection pipeline.
509
+ * The pipeline runs outside Angular's zone and only re-enters when a barcode
510
+ * is emitted, to avoid triggering change detection on every keypress.
511
+ */
512
+ constructor();
513
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BarcodeScannerDirective, never>;
514
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BarcodeScannerDirective, "[taeBarcodeScanner]", never, { "inputWindowMs": { "alias": "inputWindowMs"; "required": false; "isSignal": true; }; "terminatorKey": { "alias": "terminatorKey"; "required": false; "isSignal": true; }; }, { "barcodeScanned": "barcodeScanned"; }, never, never, true, never>;
515
+ }
516
+
467
517
  /**
468
518
  * Directive that triggers a hard page refresh when the host element is clicked
469
519
  * a specified number of times within a configurable time window.
@@ -543,5 +593,5 @@ declare class TaeFooterComponent {
543
593
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<TaeFooterComponent, "tae-footer", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "version": { "alias": "version"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
544
594
  }
545
595
 
546
- export { DEFAULT_TOAST_CONFIG, HardRefreshDirective, TOAST_CONFIG, TaeFooterComponent, ToastService, createEnvKit, parseEnvFile, provideRuntimeConfig, provideStaticConfig, provideToast };
596
+ export { BarcodeScannerDirective, DEFAULT_TOAST_CONFIG, HardRefreshDirective, TOAST_CONFIG, TaeFooterComponent, ToastService, createEnvKit, parseEnvFile, provideRuntimeConfig, provideStaticConfig, provideToast };
547
597
  export type { Toast, ToastConfig, ToastOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scania-nl/tegel-angular-extensions",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"