@scania-nl/tegel-angular-extensions 0.0.5 → 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 +159 -31
- package/esm2022/index.mjs +10 -1
- package/esm2022/lib/components/tae-footer/tae-footer.component.mjs +48 -0
- package/esm2022/lib/directives/barcode-scanner.directive.mjs +94 -0
- package/esm2022/lib/directives/hard-refresh.directive.mjs +83 -0
- package/esm2022/lib/env/angular/provide-runtime-config.mjs +3 -3
- package/esm2022/lib/toast/toast.component.mjs +5 -5
- package/esm2022/lib/toast/toast.service.mjs +3 -3
- package/index.d.ts +131 -1
- package/package.json +1 -1
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. **
|
|
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
|
|
@@ -33,27 +34,33 @@ Provides simple wrappers for toast and modal (TBC) functionality using Angular 1
|
|
|
33
34
|
|
|
34
35
|
1. [Installation](#-installation)
|
|
35
36
|
2. [Environment Configuration Overview](#-environment-configuration-overview)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
1. [Defining your schema with createEnvKit](#defining-your-schema-with-createenvkit)
|
|
38
|
+
2. [Defining Static Environments (Dev/Prod)](#defining-static-environments-devprod)
|
|
39
|
+
3. [Providing Runtime Configuration](#providing-runtime-configuration)
|
|
40
|
+
4. [Using the ENV_CONFIG Token](#using-the-env_config-token)
|
|
41
|
+
5. [Runtime Configuration Binaries](#runtime-configuration-binaries)
|
|
42
|
+
1. [`docker-entrypoint.sh`](#docker-entrypointsh)
|
|
43
|
+
2. [`extract-env-vars.sh`](#extract-env-varssh)
|
|
44
|
+
3. [`nginx.conf`](#nginxconf)
|
|
45
|
+
4. [Example Docker Commands](#example-docker-commands)
|
|
45
46
|
3. [Toasts](#-toasts)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
4. [
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
1. [Quick Start](#-quick-start)
|
|
48
|
+
1. [Add Providers](#add-providers)
|
|
49
|
+
2. [Use in components](#use-in-components)
|
|
50
|
+
2. [Configuration Options](#️-configuration-options)
|
|
51
|
+
3. [ToastService API](#-toastservice-api)
|
|
52
|
+
1. [ToastService Properties](#-toastservice-properties)
|
|
53
|
+
2. [ToastService Methods](#-toastservice-methods)
|
|
54
|
+
3. [Toast Lifecycle Hooks](#-toast-lifecycle-hooks)
|
|
55
|
+
4. [Components & Directives](#-components--directives)
|
|
56
|
+
1. [Components](#-components)
|
|
57
|
+
1. [Footer](#footer-component-tae-footer)
|
|
58
|
+
2. [Directives](#-directives)
|
|
59
|
+
1. [Hard Refresh](#hard-refresh-directive-taehardrefresh)
|
|
60
|
+
2. [Barcode Scanner](#barcode-scanner-directive-taebarcodescanner)
|
|
61
|
+
5. [Appendix](#appendix)
|
|
62
|
+
1. [Dockerfile Example](#runtime-config-dockerfile-example)
|
|
63
|
+
6. [License](#-license)
|
|
57
64
|
|
|
58
65
|
---
|
|
59
66
|
|
|
@@ -115,28 +122,33 @@ export type StaticEnv = typeof EnvKit.types.StaticEnv;
|
|
|
115
122
|
// Injection Token for EnvConfig
|
|
116
123
|
export const ENV_CONFIG = new InjectionToken<EnvConfig>('ENV_CONFIG');
|
|
117
124
|
```
|
|
125
|
+
|
|
118
126
|
|
|
119
127
|
|
|
120
128
|
## Defining Static Environments (Dev/Prod)
|
|
121
129
|
|
|
122
130
|
Initialize the environments using Nx:
|
|
131
|
+
|
|
123
132
|
```bash
|
|
124
133
|
nx g environments
|
|
125
134
|
```
|
|
126
|
-
|
|
135
|
+
|
|
136
|
+
This will create two files: `environment.ts` for the production environment configuration and `environment.development.ts` for the local development environment configuration.
|
|
127
137
|
|
|
128
138
|
`environment.development.ts`:
|
|
139
|
+
|
|
129
140
|
```ts
|
|
130
141
|
import { StaticEnv } from './environment-config';
|
|
131
142
|
|
|
132
143
|
export const environment: StaticEnv = {
|
|
133
144
|
envType: 'dev',
|
|
134
145
|
production: false,
|
|
135
|
-
apiUrl: 'https://www.company.com/api/'
|
|
146
|
+
apiUrl: 'https://www.company.com/api/',
|
|
136
147
|
} satisfies StaticEnv;
|
|
137
148
|
```
|
|
138
149
|
|
|
139
150
|
`environment.ts`:
|
|
151
|
+
|
|
140
152
|
```ts
|
|
141
153
|
import { StaticEnv } from './environment-config';
|
|
142
154
|
|
|
@@ -146,6 +158,7 @@ export const environment: StaticEnv = {
|
|
|
146
158
|
// apiUrl: 'https://www.company.com/api/' // apiUrl cannot be defined here
|
|
147
159
|
} satisfies StaticEnv;
|
|
148
160
|
```
|
|
161
|
+
|
|
149
162
|
|
|
150
163
|
|
|
151
164
|
## Providing Runtime Configuration
|
|
@@ -169,6 +182,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
169
182
|
],
|
|
170
183
|
};
|
|
171
184
|
```
|
|
185
|
+
|
|
172
186
|
|
|
173
187
|
|
|
174
188
|
## Using the ENV_CONFIG Token
|
|
@@ -186,6 +200,7 @@ export class ApiService {
|
|
|
186
200
|
}
|
|
187
201
|
}
|
|
188
202
|
```
|
|
203
|
+
|
|
189
204
|
|
|
190
205
|
This setup involves several key steps:
|
|
191
206
|
|
|
@@ -197,22 +212,27 @@ This setup involves several key steps:
|
|
|
197
212
|
> The `/env/runtime.env` file must be generated by the container during startup.
|
|
198
213
|
> Shell script binaries to support this are included the package.
|
|
199
214
|
|
|
215
|
+
|
|
216
|
+
|
|
200
217
|
## Runtime Configuration Binaries
|
|
201
218
|
|
|
202
219
|
This package ships with two lightweight shell scripts used to generate a runtime configuration file inside the container. They enable true runtime configurability without rebuilding the Angular image. Additionally, the package contains a default `nginx.conf` optimized for Angular application. The files are located in the `/docker` directory.
|
|
203
220
|
|
|
204
221
|
### `docker-entrypoint.sh`
|
|
222
|
+
|
|
205
223
|
- Entry point executed every time the container starts
|
|
206
224
|
- Calls the `extract-env-vars.sh` to generate a fresh `runtime.env`
|
|
207
225
|
- Lastly, executes the provided Dockerfile `CMD`
|
|
208
226
|
|
|
209
227
|
### `extract-env-vars.sh`
|
|
228
|
+
|
|
210
229
|
- Reads all container environment variables matching a prefix (default: `NG__`)
|
|
211
230
|
- Strips the prefix and writes cleaned `KEY=VALUE` pairs to `runtime.env`
|
|
212
|
-
- Supports nested keys via
|
|
231
|
+
- Supports nested keys via ** (e.g., `NG**myFeature\_\_myThreshold`)
|
|
213
232
|
- Defaults output to `/usr/share/nginx/html/env/runtime.env`
|
|
214
233
|
|
|
215
234
|
### `nginx.conf`
|
|
235
|
+
|
|
216
236
|
A default Nginx configuration optimized for Angular applications. It provides:
|
|
217
237
|
|
|
218
238
|
- Performance tuning for static file serving
|
|
@@ -235,10 +255,14 @@ CMD ["nginx", "-g", "daemon off;"]
|
|
|
235
255
|
|
|
236
256
|
For a complete Dockerfile example with the shipped `nginx.conf` config, refer to [Runtime Config Dockerfile Example](#runtime-config-dockerfile-example)
|
|
237
257
|
|
|
258
|
+
|
|
259
|
+
|
|
238
260
|
---
|
|
239
261
|
|
|
240
262
|
# 🔔 Toasts
|
|
241
263
|
|
|
264
|
+
A lightweight, standalone toast system that integrates seamlessly with Tegel Angular. Provides configurable, signal-driven notifications for success, error, warning, and information messages.
|
|
265
|
+
|
|
242
266
|
## 🚀 Quick Start
|
|
243
267
|
|
|
244
268
|
### Add Providers
|
|
@@ -289,6 +313,8 @@ export class MyToastDemoComponent {
|
|
|
289
313
|
|
|
290
314
|
---
|
|
291
315
|
|
|
316
|
+
|
|
317
|
+
|
|
292
318
|
## ⚙️ Configuration Options
|
|
293
319
|
|
|
294
320
|
You can configure the default appearance and behavior of toasts by passing a `ToastConfig` object to `provideToast()` in your `app.config.ts`.
|
|
@@ -308,6 +334,8 @@ All options are optional. Defaults will be applied if values are not provided.
|
|
|
308
334
|
|
|
309
335
|
---
|
|
310
336
|
|
|
337
|
+
|
|
338
|
+
|
|
311
339
|
## 🧩 ToastService API
|
|
312
340
|
|
|
313
341
|
The `ToastService` provides a signal-based API to create, manage, and dismiss toast notifications in Angular standalone apps. It is automatically available after registering `provideToast()` in your `app.config.ts`.
|
|
@@ -342,7 +370,7 @@ toastService.create({
|
|
|
342
370
|
|
|
343
371
|
Returns the unique toast ID.
|
|
344
372
|
|
|
345
|
-
|
|
373
|
+
|
|
346
374
|
|
|
347
375
|
#### Convenience Methods
|
|
348
376
|
|
|
@@ -355,13 +383,13 @@ toastService.warning({ title: 'Heads up!' });
|
|
|
355
383
|
toastService.info({ title: 'FYI' });
|
|
356
384
|
```
|
|
357
385
|
|
|
358
|
-
|
|
386
|
+
|
|
359
387
|
|
|
360
388
|
#### `getToast(id: number): Toast | undefined`
|
|
361
389
|
|
|
362
390
|
Gets a toast by its ID.
|
|
363
391
|
|
|
364
|
-
|
|
392
|
+
|
|
365
393
|
|
|
366
394
|
#### `createRandomToast(props?: Partial<ToastOptions>): number`
|
|
367
395
|
|
|
@@ -371,25 +399,25 @@ Creates a random toast with random type and title. Useful for testing. Returns t
|
|
|
371
399
|
toastService.createRandomToast();
|
|
372
400
|
```
|
|
373
401
|
|
|
374
|
-
|
|
402
|
+
|
|
375
403
|
|
|
376
404
|
#### `close(id: number): void`
|
|
377
405
|
|
|
378
406
|
Triggers the fade-out animation and schedules removal.
|
|
379
407
|
|
|
380
|
-
|
|
408
|
+
|
|
381
409
|
|
|
382
410
|
#### `closeAll(): void`
|
|
383
411
|
|
|
384
412
|
Closes all currently open toasts.
|
|
385
413
|
|
|
386
|
-
|
|
414
|
+
|
|
387
415
|
|
|
388
416
|
#### `remove(id: number): void`
|
|
389
417
|
|
|
390
418
|
Immediately removes a toast (no animation).
|
|
391
419
|
|
|
392
|
-
|
|
420
|
+
|
|
393
421
|
|
|
394
422
|
#### `removeAll(): void`
|
|
395
423
|
|
|
@@ -417,6 +445,106 @@ toastService.success({
|
|
|
417
445
|
});
|
|
418
446
|
```
|
|
419
447
|
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
# 🧩 Components & Directives
|
|
453
|
+
|
|
454
|
+
This library includes a set of standalone UI components and utility directives, all prefixed with **`tae`**, designed to extend and complement the Tegel Angular ecosystem. Each piece is lightweight, fully typed, and easy to import into any Angular 19+ application.
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
## 🧱 Components
|
|
458
|
+
|
|
459
|
+
### Footer Component (`tae-footer`)
|
|
460
|
+
|
|
461
|
+
`TaeFooterComponent` is an enhanced footer based on the Tegel `TdsFooterComponent`. It preserves the same visual appearance while adding two key improvements:
|
|
462
|
+
|
|
463
|
+
- A **compact “small” variant** for constrained layouts.
|
|
464
|
+
- An optional **version display**, allowing applications to show their build or release version directly in the footer.
|
|
465
|
+
|
|
466
|
+
This makes it ideal for full-viewport layouts that benefit from space efficiency and clear version visibility.
|
|
467
|
+
|
|
468
|
+
**Inputs:**
|
|
469
|
+
|
|
470
|
+
| Input | Type | Default | Description |
|
|
471
|
+
| ------- | --------------------- | ------- | ------------------------------------------------------------------------------------------ |
|
|
472
|
+
| variant | `'normal' \| 'small'` | normal | Layout style of the footer. `'small'` produces a more compact version. |
|
|
473
|
+
| version | `string \| undefined` | — | Optional application version string. If provided, it is displayed left of the Scania logo. |
|
|
474
|
+
|
|
475
|
+
**Example:**
|
|
476
|
+
|
|
477
|
+
```html
|
|
478
|
+
<tae-footer variant="small" version="v1.0.0" />
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
## ⚡ Directives
|
|
484
|
+
|
|
485
|
+
### Hard Refresh Directive (`taeHardRefresh`)
|
|
486
|
+
|
|
487
|
+
The `HardRefreshDirective` provides a small UX shortcut: it performs a **full page reload** when the host element is clicked **N times in rapid succession**, where each click must occur within `clickWindowMs` milliseconds of the previous one.
|
|
488
|
+
|
|
489
|
+
This is especially useful for hard-reloading tablet or mobile applications which are locked in full-screen mode, and thus have no browser buttons like refresh.
|
|
490
|
+
|
|
491
|
+
**Inputs:**
|
|
492
|
+
|
|
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. |
|
|
497
|
+
|
|
498
|
+
**Example:**
|
|
499
|
+
|
|
500
|
+
```html
|
|
501
|
+
<tds-header-brand-symbol
|
|
502
|
+
taeHardRefresh
|
|
503
|
+
[clicksRequired]="3"
|
|
504
|
+
[clickWindowMs]="500"
|
|
505
|
+
>
|
|
506
|
+
<a aria-label="Scania - red gryphon on blue shield"></a>
|
|
507
|
+
</tds-header-brand-symbol>
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
|
|
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
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
|
|
420
548
|
# Appendix
|
|
421
549
|
|
|
422
550
|
## Runtime Config Dockerfile Example
|
package/esm2022/index.mjs
CHANGED
|
@@ -13,4 +13,13 @@ export { parseEnvFile } from './lib/env/core/parse-env-file';
|
|
|
13
13
|
// Angular
|
|
14
14
|
export { provideRuntimeConfig } from './lib/env/angular/provide-runtime-config';
|
|
15
15
|
export { provideStaticConfig } from './lib/env/angular/provide-static-config';
|
|
16
|
-
|
|
16
|
+
// --------------------------------------------------
|
|
17
|
+
// Directive Module Exports
|
|
18
|
+
// --------------------------------------------------
|
|
19
|
+
export { BarcodeScannerDirective } from './lib/directives/barcode-scanner.directive';
|
|
20
|
+
export { HardRefreshDirective } from './lib/directives/hard-refresh.directive';
|
|
21
|
+
// --------------------------------------------------
|
|
22
|
+
// Component Module Exports
|
|
23
|
+
// --------------------------------------------------
|
|
24
|
+
export { TaeFooterComponent } from './lib/components/tae-footer/tae-footer.component';
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWJzL3RlZ2VsLWFuZ3VsYXItZXh0ZW5zaW9ucy9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEscURBQXFEO0FBQ3JELHVCQUF1QjtBQUN2QixxREFBcUQ7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsWUFBWSxHQUViLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBS3pELHFEQUFxRDtBQUNyRCxxQkFBcUI7QUFDckIscURBQXFEO0FBQ3JELE9BQU87QUFDUCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTdELFVBQVU7QUFDVixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUNoRixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUU5RSxxREFBcUQ7QUFDckQsMkJBQTJCO0FBQzNCLHFEQUFxRDtBQUNyRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUNyRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUUvRSxxREFBcUQ7QUFDckQsMkJBQTJCO0FBQzNCLHFEQUFxRDtBQUNyRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxrREFBa0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBUb2FzdCBNb2R1bGUgRXhwb3J0c1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbmV4cG9ydCB7IHByb3ZpZGVUb2FzdCB9IGZyb20gJy4vbGliL3RvYXN0L3Byb3ZpZGUtdG9hc3QnO1xuZXhwb3J0IHtcbiAgREVGQVVMVF9UT0FTVF9DT05GSUcsXG4gIFRPQVNUX0NPTkZJRyxcbiAgVG9hc3RDb25maWcsXG59IGZyb20gJy4vbGliL3RvYXN0L3RvYXN0LmNvbmZpZyc7XG5leHBvcnQgeyBUb2FzdFNlcnZpY2UgfSBmcm9tICcuL2xpYi90b2FzdC90b2FzdC5zZXJ2aWNlJztcblxuLy8gTW9kZWxzXG5leHBvcnQgeyBUb2FzdCwgVG9hc3RPcHRpb25zIH0gZnJvbSAnLi9saWIvdG9hc3QvbW9kZWxzL3RvYXN0Lm1vZGVsJztcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEVudiBNb2R1bGUgRXhwb3J0c1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIENvcmVcbmV4cG9ydCB7IGNyZWF0ZUVudktpdCB9IGZyb20gJy4vbGliL2Vudi9jb3JlL2NyZWF0ZS1lbnYta2l0JztcbmV4cG9ydCB7IHBhcnNlRW52RmlsZSB9IGZyb20gJy4vbGliL2Vudi9jb3JlL3BhcnNlLWVudi1maWxlJztcblxuLy8gQW5ndWxhclxuZXhwb3J0IHsgcHJvdmlkZVJ1bnRpbWVDb25maWcgfSBmcm9tICcuL2xpYi9lbnYvYW5ndWxhci9wcm92aWRlLXJ1bnRpbWUtY29uZmlnJztcbmV4cG9ydCB7IHByb3ZpZGVTdGF0aWNDb25maWcgfSBmcm9tICcuL2xpYi9lbnYvYW5ndWxhci9wcm92aWRlLXN0YXRpYy1jb25maWcnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gRGlyZWN0aXZlIE1vZHVsZSBFeHBvcnRzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0IHsgQmFyY29kZVNjYW5uZXJEaXJlY3RpdmUgfSBmcm9tICcuL2xpYi9kaXJlY3RpdmVzL2JhcmNvZGUtc2Nhbm5lci5kaXJlY3RpdmUnO1xuZXhwb3J0IHsgSGFyZFJlZnJlc2hEaXJlY3RpdmUgfSBmcm9tICcuL2xpYi9kaXJlY3RpdmVzL2hhcmQtcmVmcmVzaC5kaXJlY3RpdmUnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gQ29tcG9uZW50IE1vZHVsZSBFeHBvcnRzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0IHsgVGFlRm9vdGVyQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy90YWUtZm9vdGVyL3RhZS1mb290ZXIuY29tcG9uZW50JztcbiJdfQ==
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/common";
|
|
5
|
+
/**
|
|
6
|
+
* An enhanced standalone footer component based on the Tegel `TdsFooterComponent`.
|
|
7
|
+
*
|
|
8
|
+
* `TaeFooterComponent` maintains the same visual style as the original
|
|
9
|
+
* Tegel footer, but adds two key improvements:
|
|
10
|
+
*
|
|
11
|
+
* - A **compact “small” variant** for layouts where vertical space is limited.
|
|
12
|
+
* - Optional **version display**, allowing applications to show their
|
|
13
|
+
* build or release version directly in the footer.
|
|
14
|
+
*
|
|
15
|
+
* Example:
|
|
16
|
+
* ```html
|
|
17
|
+
* <tae-footer variant="small" version="v1.0.0" />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export class TaeFooterComponent {
|
|
21
|
+
/**
|
|
22
|
+
* Determines the visual layout of the footer.
|
|
23
|
+
*
|
|
24
|
+
* - `"normal"` displays the standard footer layout (aligned to tds-footer)
|
|
25
|
+
* - `"small"` produces a more compact version suitable for tight layouts
|
|
26
|
+
*
|
|
27
|
+
* @default 'normal'
|
|
28
|
+
*/
|
|
29
|
+
variant = input('normal');
|
|
30
|
+
/**
|
|
31
|
+
* Optional application version string to display in the footer.
|
|
32
|
+
*
|
|
33
|
+
* When provided, it appears left of the Scania logo.
|
|
34
|
+
* If omitted or `undefined`, the version section is not shown.
|
|
35
|
+
*/
|
|
36
|
+
version = input();
|
|
37
|
+
/**
|
|
38
|
+
* The current year used in the copyright section.
|
|
39
|
+
*/
|
|
40
|
+
currentYear = new Date().getFullYear();
|
|
41
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TaeFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
42
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: TaeFooterComponent, isStandalone: true, selector: "tae-footer", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, version: { classPropertyName: "version", publicName: "version", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let versionValue = version();\n<footer>\n <div class=\"footer-main\" [ngClass]=\"'footer-' + variant()\">\n <div class=\"footer-main-bottom\">\n <small class=\"copyright\">Copyright © {{ currentYear }} Scania</small>\n @if (versionValue) {\n <small class=\"version\">Version: {{ versionValue }}</small>\n }\n <div class=\"brand\"></div>\n </div>\n </div>\n</footer>\n", styles: [":host{display:grid}.footer-main{background-color:var(--tds-footer-main-background);padding:0 40px}.footer-main .footer-main-bottom{padding:40px 0;display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--tds-footer-main-divider)}.footer-main .footer-main-bottom .copyright,.footer-main .footer-main-bottom .version{margin:0;font:var(--tds-detail-02);letter-spacing:var(--tds-detail-02-ls);color:var(--tds-footer-main-copyright)}.footer-main .footer-main-bottom .version{font-size:smaller;margin-left:auto;margin-right:40px}.footer-main .footer-main-bottom .brand{background-image:var(--tds-background-image-scania-wordmark-white-svg);background-repeat:no-repeat;background-size:117px;background-position:right center;width:117px;height:20px}.footer-main.footer-small .footer-main-bottom{padding:20px 0}.footer-main.footer-small .footer-main-bottom .copyright{font:var(--tds-detail-05);letter-spacing:var(--tds-detail-05-ls)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
43
|
+
}
|
|
44
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: TaeFooterComponent, decorators: [{
|
|
45
|
+
type: Component,
|
|
46
|
+
args: [{ selector: 'tae-footer', imports: [CommonModule], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let versionValue = version();\n<footer>\n <div class=\"footer-main\" [ngClass]=\"'footer-' + variant()\">\n <div class=\"footer-main-bottom\">\n <small class=\"copyright\">Copyright © {{ currentYear }} Scania</small>\n @if (versionValue) {\n <small class=\"version\">Version: {{ versionValue }}</small>\n }\n <div class=\"brand\"></div>\n </div>\n </div>\n</footer>\n", styles: [":host{display:grid}.footer-main{background-color:var(--tds-footer-main-background);padding:0 40px}.footer-main .footer-main-bottom{padding:40px 0;display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--tds-footer-main-divider)}.footer-main .footer-main-bottom .copyright,.footer-main .footer-main-bottom .version{margin:0;font:var(--tds-detail-02);letter-spacing:var(--tds-detail-02-ls);color:var(--tds-footer-main-copyright)}.footer-main .footer-main-bottom .version{font-size:smaller;margin-left:auto;margin-right:40px}.footer-main .footer-main-bottom .brand{background-image:var(--tds-background-image-scania-wordmark-white-svg);background-repeat:no-repeat;background-size:117px;background-position:right center;width:117px;height:20px}.footer-main.footer-small .footer-main-bottom{padding:20px 0}.footer-main.footer-small .footer-main-bottom .copyright{font:var(--tds-detail-05);letter-spacing:var(--tds-detail-05-ls)}\n"] }]
|
|
47
|
+
}] });
|
|
48
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFlLWZvb3Rlci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9saWJzL3RlZ2VsLWFuZ3VsYXItZXh0ZW5zaW9ucy9zcmMvbGliL2NvbXBvbmVudHMvdGFlLWZvb3Rlci90YWUtZm9vdGVyLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvdGVnZWwtYW5ndWxhci1leHRlbnNpb25zL3NyYy9saWIvY29tcG9uZW50cy90YWUtZm9vdGVyL3RhZS1mb290ZXIuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sZUFBZSxDQUFDOzs7QUFFMUU7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFTSCxNQUFNLE9BQU8sa0JBQWtCO0lBQzdCOzs7Ozs7O09BT0c7SUFFTSxPQUFPLEdBQUcsS0FBSyxDQUFxQixRQUFRLENBQUMsQ0FBQztJQUN2RDs7Ozs7T0FLRztJQUNNLE9BQU8sR0FBRyxLQUFLLEVBQXNCLENBQUM7SUFFL0M7O09BRUc7SUFDTSxXQUFXLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQzt3R0F0QnJDLGtCQUFrQjs0RkFBbEIsa0JBQWtCLDhVQzFCL0IsMFpBWUEscy9CRFFZLFlBQVk7OzRGQU1YLGtCQUFrQjtrQkFSOUIsU0FBUzsrQkFDRSxZQUFZLFdBQ2IsQ0FBQyxZQUFZLENBQUMsY0FDWCxJQUFJLG1CQUdDLHVCQUF1QixDQUFDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgaW5wdXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuLyoqXG4gKiBBbiBlbmhhbmNlZCBzdGFuZGFsb25lIGZvb3RlciBjb21wb25lbnQgYmFzZWQgb24gdGhlIFRlZ2VsIGBUZHNGb290ZXJDb21wb25lbnRgLlxuICpcbiAqIGBUYWVGb290ZXJDb21wb25lbnRgIG1haW50YWlucyB0aGUgc2FtZSB2aXN1YWwgc3R5bGUgYXMgdGhlIG9yaWdpbmFsXG4gKiBUZWdlbCBmb290ZXIsIGJ1dCBhZGRzIHR3byBrZXkgaW1wcm92ZW1lbnRzOlxuICpcbiAqIC0gQSAqKmNvbXBhY3Qg4oCcc21hbGzigJ0gdmFyaWFudCoqIGZvciBsYXlvdXRzIHdoZXJlIHZlcnRpY2FsIHNwYWNlIGlzIGxpbWl0ZWQuXG4gKiAtIE9wdGlvbmFsICoqdmVyc2lvbiBkaXNwbGF5KiosIGFsbG93aW5nIGFwcGxpY2F0aW9ucyB0byBzaG93IHRoZWlyXG4gKiAgIGJ1aWxkIG9yIHJlbGVhc2UgdmVyc2lvbiBkaXJlY3RseSBpbiB0aGUgZm9vdGVyLlxuICpcbiAqIEV4YW1wbGU6XG4gKiBgYGBodG1sXG4gKiA8dGFlLWZvb3RlciB2YXJpYW50PVwic21hbGxcIiB2ZXJzaW9uPVwidjEuMC4wXCIgLz5cbiAqIGBgYFxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd0YWUtZm9vdGVyJyxcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZV0sXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIHRlbXBsYXRlVXJsOiAnLi90YWUtZm9vdGVyLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdGFlLWZvb3Rlci5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVGFlRm9vdGVyQ29tcG9uZW50IHtcbiAgLyoqXG4gICAqIERldGVybWluZXMgdGhlIHZpc3VhbCBsYXlvdXQgb2YgdGhlIGZvb3Rlci5cbiAgICpcbiAgICogLSBgXCJub3JtYWxcImAgZGlzcGxheXMgdGhlIHN0YW5kYXJkIGZvb3RlciBsYXlvdXQgKGFsaWduZWQgdG8gdGRzLWZvb3RlcilcbiAgICogLSBgXCJzbWFsbFwiYCBwcm9kdWNlcyBhIG1vcmUgY29tcGFjdCB2ZXJzaW9uIHN1aXRhYmxlIGZvciB0aWdodCBsYXlvdXRzXG4gICAqXG4gICAqIEBkZWZhdWx0ICdub3JtYWwnXG4gICAqL1xuXG4gIHJlYWRvbmx5IHZhcmlhbnQgPSBpbnB1dDwnbm9ybWFsJyB8ICdzbWFsbCc+KCdub3JtYWwnKTtcbiAgLyoqXG4gICAqIE9wdGlvbmFsIGFwcGxpY2F0aW9uIHZlcnNpb24gc3RyaW5nIHRvIGRpc3BsYXkgaW4gdGhlIGZvb3Rlci5cbiAgICpcbiAgICogV2hlbiBwcm92aWRlZCwgaXQgYXBwZWFycyBsZWZ0IG9mIHRoZSBTY2FuaWEgbG9nby5cbiAgICogSWYgb21pdHRlZCBvciBgdW5kZWZpbmVkYCwgdGhlIHZlcnNpb24gc2VjdGlvbiBpcyBub3Qgc2hvd24uXG4gICAqL1xuICByZWFkb25seSB2ZXJzaW9uID0gaW5wdXQ8c3RyaW5nIHwgdW5kZWZpbmVkPigpO1xuXG4gIC8qKlxuICAgKiBUaGUgY3VycmVudCB5ZWFyIHVzZWQgaW4gdGhlIGNvcHlyaWdodCBzZWN0aW9uLlxuICAgKi9cbiAgcmVhZG9ubHkgY3VycmVudFllYXIgPSBuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCk7XG59XG4iLCJAbGV0IHZlcnNpb25WYWx1ZSA9IHZlcnNpb24oKTtcbjxmb290ZXI+XG4gIDxkaXYgY2xhc3M9XCJmb290ZXItbWFpblwiIFtuZ0NsYXNzXT1cIidmb290ZXItJyArIHZhcmlhbnQoKVwiPlxuICAgIDxkaXYgY2xhc3M9XCJmb290ZXItbWFpbi1ib3R0b21cIj5cbiAgICAgIDxzbWFsbCBjbGFzcz1cImNvcHlyaWdodFwiPkNvcHlyaWdodCAmY29weTsge3sgY3VycmVudFllYXIgfX0gU2NhbmlhPC9zbWFsbD5cbiAgICAgIEBpZiAodmVyc2lvblZhbHVlKSB7XG4gICAgICAgIDxzbWFsbCBjbGFzcz1cInZlcnNpb25cIj5WZXJzaW9uOiB7eyB2ZXJzaW9uVmFsdWUgfX08L3NtYWxsPlxuICAgICAgfVxuICAgICAgPGRpdiBjbGFzcz1cImJyYW5kXCI+PC9kaXY+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuPC9mb290ZXI+XG4iXX0=
|
|
@@ -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
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Directive, input } from '@angular/core';
|
|
2
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { filter, map, pairwise, scan, startWith, Subject, throttleTime, timestamp, } from 'rxjs';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Directive that triggers a hard page refresh when the host element is clicked
|
|
7
|
+
* a specified number of times within a configurable time window.
|
|
8
|
+
*
|
|
9
|
+
* This is useful for adding developer-oriented shortcuts, diagnostics entry points,
|
|
10
|
+
* hidden "triple-click to reload" behaviors, or fallback mechanisms in production.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```html
|
|
14
|
+
* <div taeHardRefresh [clicksRequired]="3" [clickWindowMs]="500">...</div>
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export class HardRefreshDirective {
|
|
18
|
+
/**
|
|
19
|
+
* The number of rapid consecutive clicks required to trigger a hard refresh.
|
|
20
|
+
* A "rapid" click is one that occurs within `clickWindowMs` of the previous click.
|
|
21
|
+
*
|
|
22
|
+
* @default 3
|
|
23
|
+
*/
|
|
24
|
+
clicksRequired = input(3);
|
|
25
|
+
/**
|
|
26
|
+
* The maximum time interval (in milliseconds) allowed between consecutive clicks
|
|
27
|
+
* for them to be considered part of the same "streak".
|
|
28
|
+
*
|
|
29
|
+
* If the interval between two clicks exceeds this value, the streak resets.
|
|
30
|
+
*
|
|
31
|
+
* @default 500
|
|
32
|
+
*/
|
|
33
|
+
clickWindowMs = input(500);
|
|
34
|
+
/**
|
|
35
|
+
* Internal click event stream used to build the rapid-click detection pipeline.
|
|
36
|
+
* Emits one value per host click (via the `(click)` host listener).
|
|
37
|
+
*/
|
|
38
|
+
clicks$ = new Subject();
|
|
39
|
+
constructor() {
|
|
40
|
+
/**
|
|
41
|
+
* Rapid-click detection pipeline:
|
|
42
|
+
*
|
|
43
|
+
* - Timestamps each click.
|
|
44
|
+
* - Compares consecutive timestamps to determine whether they fall within the
|
|
45
|
+
* configured `clickWindowMs` threshold.
|
|
46
|
+
* - Maintains a streak count using a reducer (`scan`).
|
|
47
|
+
* - Emits once the streak count reaches or exceeds `clicksRequired`.
|
|
48
|
+
* - Uses `throttleTime` to prevent duplicate triggers within the same burst.
|
|
49
|
+
*/
|
|
50
|
+
this.clicks$
|
|
51
|
+
.pipe(takeUntilDestroyed(),
|
|
52
|
+
// Turn click events into raw timestamps (epoch ms)
|
|
53
|
+
timestamp(), map((t) => t.timestamp),
|
|
54
|
+
// Work in pairs: [previousTimestamp, currentTimestamp]
|
|
55
|
+
startWith(0), pairwise(),
|
|
56
|
+
// Maintain a streak of clicks within the time window
|
|
57
|
+
scan((acc, [prev, curr]) => curr - prev <= this.clickWindowMs() ? acc + 1 : 1, 0),
|
|
58
|
+
// Only continue when we reach the threshold number of clicks
|
|
59
|
+
filter((streak) => streak >= this.clicksRequired()),
|
|
60
|
+
// Avoid multiple triggers for the same "burst" of clicks
|
|
61
|
+
throttleTime(this.clickWindowMs(), undefined, {
|
|
62
|
+
leading: true,
|
|
63
|
+
trailing: false,
|
|
64
|
+
}))
|
|
65
|
+
.subscribe(() => {
|
|
66
|
+
// Reload the window
|
|
67
|
+
window.location.reload();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: HardRefreshDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
71
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.17", type: HardRefreshDirective, isStandalone: true, selector: "[taeHardRefresh]", inputs: { clicksRequired: { classPropertyName: "clicksRequired", publicName: "clicksRequired", isSignal: true, isRequired: false, transformFunction: null }, clickWindowMs: { classPropertyName: "clickWindowMs", publicName: "clickWindowMs", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "clicks$.next()" } }, ngImport: i0 });
|
|
72
|
+
}
|
|
73
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: HardRefreshDirective, decorators: [{
|
|
74
|
+
type: Directive,
|
|
75
|
+
args: [{
|
|
76
|
+
selector: '[taeHardRefresh]',
|
|
77
|
+
standalone: true,
|
|
78
|
+
host: {
|
|
79
|
+
'(click)': 'clicks$.next()',
|
|
80
|
+
},
|
|
81
|
+
}]
|
|
82
|
+
}], ctorParameters: () => [] });
|
|
83
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFyZC1yZWZyZXNoLmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvdGVnZWwtYW5ndWxhci1leHRlbnNpb25zL3NyYy9saWIvZGlyZWN0aXZlcy9oYXJkLXJlZnJlc2guZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2pELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ2hFLE9BQU8sRUFDTCxNQUFNLEVBQ04sR0FBRyxFQUNILFFBQVEsRUFDUixJQUFJLEVBQ0osU0FBUyxFQUNULE9BQU8sRUFDUCxZQUFZLEVBQ1osU0FBUyxHQUNWLE1BQU0sTUFBTSxDQUFDOztBQUVkOzs7Ozs7Ozs7OztHQVdHO0FBUUgsTUFBTSxPQUFPLG9CQUFvQjtJQUMvQjs7Ozs7T0FLRztJQUNNLGNBQWMsR0FBRyxLQUFLLENBQVMsQ0FBQyxDQUFDLENBQUM7SUFFM0M7Ozs7Ozs7T0FPRztJQUNNLGFBQWEsR0FBRyxLQUFLLENBQVMsR0FBRyxDQUFDLENBQUM7SUFFNUM7OztPQUdHO0lBQ2dCLE9BQU8sR0FBRyxJQUFJLE9BQU8sRUFBUSxDQUFDO0lBRWpEO1FBQ0U7Ozs7Ozs7OztXQVNHO1FBQ0gsSUFBSSxDQUFDLE9BQU87YUFDVCxJQUFJLENBQ0gsa0JBQWtCLEVBQUU7UUFDcEIsbURBQW1EO1FBQ25ELFNBQVMsRUFBRSxFQUNYLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztRQUN2Qix1REFBdUQ7UUFDdkQsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUNaLFFBQVEsRUFBRTtRQUNWLHFEQUFxRDtRQUNyRCxJQUFJLENBQ0YsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUNwQixJQUFJLEdBQUcsSUFBSSxJQUFJLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUNuRCxDQUFDLENBQ0Y7UUFDRCw2REFBNkQ7UUFDN0QsTUFBTSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ25ELHlEQUF5RDtRQUN6RCxZQUFZLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxFQUFFLFNBQVMsRUFBRTtZQUM1QyxPQUFPLEVBQUUsSUFBSTtZQUNiLFFBQVEsRUFBRSxLQUFLO1NBQ2hCLENBQUMsQ0FDSDthQUNBLFNBQVMsQ0FBQyxHQUFHLEVBQUU7WUFDZCxvQkFBb0I7WUFDcEIsTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUMzQixDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7d0dBL0RVLG9CQUFvQjs0RkFBcEIsb0JBQW9COzs0RkFBcEIsb0JBQW9CO2tCQVBoQyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSxrQkFBa0I7b0JBQzVCLFVBQVUsRUFBRSxJQUFJO29CQUNoQixJQUFJLEVBQUU7d0JBQ0osU0FBUyxFQUFFLGdCQUFnQjtxQkFDNUI7aUJBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBEaXJlY3RpdmUsIGlucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyB0YWtlVW50aWxEZXN0cm95ZWQgfSBmcm9tICdAYW5ndWxhci9jb3JlL3J4anMtaW50ZXJvcCc7XG5pbXBvcnQge1xuICBmaWx0ZXIsXG4gIG1hcCxcbiAgcGFpcndpc2UsXG4gIHNjYW4sXG4gIHN0YXJ0V2l0aCxcbiAgU3ViamVjdCxcbiAgdGhyb3R0bGVUaW1lLFxuICB0aW1lc3RhbXAsXG59IGZyb20gJ3J4anMnO1xuXG4vKipcbiAqIERpcmVjdGl2ZSB0aGF0IHRyaWdnZXJzIGEgaGFyZCBwYWdlIHJlZnJlc2ggd2hlbiB0aGUgaG9zdCBlbGVtZW50IGlzIGNsaWNrZWRcbiAqIGEgc3BlY2lmaWVkIG51bWJlciBvZiB0aW1lcyB3aXRoaW4gYSBjb25maWd1cmFibGUgdGltZSB3aW5kb3cuXG4gKlxuICogVGhpcyBpcyB1c2VmdWwgZm9yIGFkZGluZyBkZXZlbG9wZXItb3JpZW50ZWQgc2hvcnRjdXRzLCBkaWFnbm9zdGljcyBlbnRyeSBwb2ludHMsXG4gKiBoaWRkZW4gXCJ0cmlwbGUtY2xpY2sgdG8gcmVsb2FkXCIgYmVoYXZpb3JzLCBvciBmYWxsYmFjayBtZWNoYW5pc21zIGluIHByb2R1Y3Rpb24uXG4gKlxuICogVXNhZ2U6XG4gKiBgYGBodG1sXG4gKiA8ZGl2IHRhZUhhcmRSZWZyZXNoIFtjbGlja3NSZXF1aXJlZF09XCIzXCIgW2NsaWNrV2luZG93TXNdPVwiNTAwXCI+Li4uPC9kaXY+XG4gKiBgYGBcbiAqL1xuQERpcmVjdGl2ZSh7XG4gIHNlbGVjdG9yOiAnW3RhZUhhcmRSZWZyZXNoXScsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGhvc3Q6IHtcbiAgICAnKGNsaWNrKSc6ICdjbGlja3MkLm5leHQoKScsXG4gIH0sXG59KVxuZXhwb3J0IGNsYXNzIEhhcmRSZWZyZXNoRGlyZWN0aXZlIHtcbiAgLyoqXG4gICAqIFRoZSBudW1iZXIgb2YgcmFwaWQgY29uc2VjdXRpdmUgY2xpY2tzIHJlcXVpcmVkIHRvIHRyaWdnZXIgYSBoYXJkIHJlZnJlc2guXG4gICAqIEEgXCJyYXBpZFwiIGNsaWNrIGlzIG9uZSB0aGF0IG9jY3VycyB3aXRoaW4gYGNsaWNrV2luZG93TXNgIG9mIHRoZSBwcmV2aW91cyBjbGljay5cbiAgICpcbiAgICogQGRlZmF1bHQgM1xuICAgKi9cbiAgcmVhZG9ubHkgY2xpY2tzUmVxdWlyZWQgPSBpbnB1dDxudW1iZXI+KDMpO1xuXG4gIC8qKlxuICAgKiBUaGUgbWF4aW11bSB0aW1lIGludGVydmFsIChpbiBtaWxsaXNlY29uZHMpIGFsbG93ZWQgYmV0d2VlbiBjb25zZWN1dGl2ZSBjbGlja3NcbiAgICogZm9yIHRoZW0gdG8gYmUgY29uc2lkZXJlZCBwYXJ0IG9mIHRoZSBzYW1lIFwic3RyZWFrXCIuXG4gICAqXG4gICAqIElmIHRoZSBpbnRlcnZhbCBiZXR3ZWVuIHR3byBjbGlja3MgZXhjZWVkcyB0aGlzIHZhbHVlLCB0aGUgc3RyZWFrIHJlc2V0cy5cbiAgICpcbiAgICogQGRlZmF1bHQgNTAwXG4gICAqL1xuICByZWFkb25seSBjbGlja1dpbmRvd01zID0gaW5wdXQ8bnVtYmVyPig1MDApO1xuXG4gIC8qKlxuICAgKiBJbnRlcm5hbCBjbGljayBldmVudCBzdHJlYW0gdXNlZCB0byBidWlsZCB0aGUgcmFwaWQtY2xpY2sgZGV0ZWN0aW9uIHBpcGVsaW5lLlxuICAgKiBFbWl0cyBvbmUgdmFsdWUgcGVyIGhvc3QgY2xpY2sgKHZpYSB0aGUgYChjbGljaylgIGhvc3QgbGlzdGVuZXIpLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNsaWNrcyQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIC8qKlxuICAgICAqIFJhcGlkLWNsaWNrIGRldGVjdGlvbiBwaXBlbGluZTpcbiAgICAgKlxuICAgICAqIC0gVGltZXN0YW1wcyBlYWNoIGNsaWNrLlxuICAgICAqIC0gQ29tcGFyZXMgY29uc2VjdXRpdmUgdGltZXN0YW1wcyB0byBkZXRlcm1pbmUgd2hldGhlciB0aGV5IGZhbGwgd2l0aGluIHRoZVxuICAgICAqICAgY29uZmlndXJlZCBgY2xpY2tXaW5kb3dNc2AgdGhyZXNob2xkLlxuICAgICAqIC0gTWFpbnRhaW5zIGEgc3RyZWFrIGNvdW50IHVzaW5nIGEgcmVkdWNlciAoYHNjYW5gKS5cbiAgICAgKiAtIEVtaXRzIG9uY2UgdGhlIHN0cmVhayBjb3VudCByZWFjaGVzIG9yIGV4Y2VlZHMgYGNsaWNrc1JlcXVpcmVkYC5cbiAgICAgKiAtIFVzZXMgYHRocm90dGxlVGltZWAgdG8gcHJldmVudCBkdXBsaWNhdGUgdHJpZ2dlcnMgd2l0aGluIHRoZSBzYW1lIGJ1cnN0LlxuICAgICAqL1xuICAgIHRoaXMuY2xpY2tzJFxuICAgICAgLnBpcGUoXG4gICAgICAgIHRha2VVbnRpbERlc3Ryb3llZCgpLFxuICAgICAgICAvLyBUdXJuIGNsaWNrIGV2ZW50cyBpbnRvIHJhdyB0aW1lc3RhbXBzIChlcG9jaCBtcylcbiAgICAgICAgdGltZXN0YW1wKCksXG4gICAgICAgIG1hcCgodCkgPT4gdC50aW1lc3RhbXApLFxuICAgICAgICAvLyBXb3JrIGluIHBhaXJzOiBbcHJldmlvdXNUaW1lc3RhbXAsIGN1cnJlbnRUaW1lc3RhbXBdXG4gICAgICAgIHN0YXJ0V2l0aCgwKSxcbiAgICAgICAgcGFpcndpc2UoKSxcbiAgICAgICAgLy8gTWFpbnRhaW4gYSBzdHJlYWsgb2YgY2xpY2tzIHdpdGhpbiB0aGUgdGltZSB3aW5kb3dcbiAgICAgICAgc2NhbihcbiAgICAgICAgICAoYWNjLCBbcHJldiwgY3Vycl0pID0+XG4gICAgICAgICAgICBjdXJyIC0gcHJldiA8PSB0aGlzLmNsaWNrV2luZG93TXMoKSA/IGFjYyArIDEgOiAxLFxuICAgICAgICAgIDBcbiAgICAgICAgKSxcbiAgICAgICAgLy8gT25seSBjb250aW51ZSB3aGVuIHdlIHJlYWNoIHRoZSB0aHJlc2hvbGQgbnVtYmVyIG9mIGNsaWNrc1xuICAgICAgICBmaWx0ZXIoKHN0cmVhaykgPT4gc3RyZWFrID49IHRoaXMuY2xpY2tzUmVxdWlyZWQoKSksXG4gICAgICAgIC8vIEF2b2lkIG11bHRpcGxlIHRyaWdnZXJzIGZvciB0aGUgc2FtZSBcImJ1cnN0XCIgb2YgY2xpY2tzXG4gICAgICAgIHRocm90dGxlVGltZSh0aGlzLmNsaWNrV2luZG93TXMoKSwgdW5kZWZpbmVkLCB7XG4gICAgICAgICAgbGVhZGluZzogdHJ1ZSxcbiAgICAgICAgICB0cmFpbGluZzogZmFsc2UsXG4gICAgICAgIH0pXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgLy8gUmVsb2FkIHRoZSB3aW5kb3dcbiAgICAgICAgd2luZG93LmxvY2F0aW9uLnJlbG9hZCgpO1xuICAgICAgfSk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -138,10 +138,10 @@ export function provideRuntimeConfig(kit, staticEnv, { envPath = '/env/runtime.e
|
|
|
138
138
|
this.config ??= staticEnv;
|
|
139
139
|
return this.config;
|
|
140
140
|
}
|
|
141
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
142
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.
|
|
141
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RuntimeConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
142
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RuntimeConfigService, providedIn: 'root' });
|
|
143
143
|
}
|
|
144
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
144
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RuntimeConfigService, decorators: [{
|
|
145
145
|
type: Injectable,
|
|
146
146
|
args: [{ providedIn: 'root' }]
|
|
147
147
|
}] });
|
|
@@ -24,11 +24,11 @@ export class ToastComponent {
|
|
|
24
24
|
* @returns void
|
|
25
25
|
*/
|
|
26
26
|
closeToast = (toast) => this.toastService.close(toast.id);
|
|
27
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
28
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.
|
|
27
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
28
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: ToastComponent, isStandalone: true, selector: "tae-toast", ngImport: i0, template: "<ul class=\"toast-list\" aria-live=\"polite\" aria-atomic=\"true\">\n @for (toast of toastsSignal(); track toast.id) {\n <li\n class=\"toast-item\"\n [class]=\"'toast-' + toast.type\"\n [class.toast-closing]=\"toast.state === 'closing'\"\n [style.--duration.ms]=\"toast.duration\"\n [style.--close-duration.ms]=\"toast.closeDuration\"\n role=\"status\"\n >\n <tds-toast\n [variant]=\"toast.type\"\n [header]=\"toast.title\"\n [subheader]=\"toast.description\"\n [closable]=\"toast.closable\"\n tds-close-aria-label=\"Toast close button\"\n >\n @if (toast.link) {\n <tds-link slot=\"actions\">\n <a [routerLink]=\"toast.link\">\n {{ toast.linkText ?? 'Click here' }}\n </a>\n </tds-link>\n } @else if (toast.action) {\n <tds-link slot=\"actions\">\n <a href=\"#\" (click)=\"toast.action()\">\n {{ toast.linkText ?? 'Click here' }}\n </a>\n </tds-link>\n }\n </tds-toast>\n @if (toast.closable) {\n <button\n class=\"toast-close\"\n (click)=\"closeToast(toast)\"\n aria-label=\"Close toast\"\n ></button>\n }\n </li>\n }\n</ul>\n", styles: [":host{position:fixed;right:0;bottom:0;overflow:hidden;z-index:9999}.toast-list{display:grid;grid-template-columns:1fr;gap:6px;list-style:none;padding:6px;margin:0}.toast-item{position:relative;animation:fadeIn .3s ease-in forwards}.toast-item.toast-closing{animation-name:fadeOut;animation-duration:var(--close-duration, .3s);animation-fill-mode:forwards;pointer-events:none}.toast-item:before{content:\"\";position:absolute;height:3px;width:100%;bottom:0;left:4px;right:0;animation:progress var(--duration, 7s) linear forwards;border-top-right-radius:4px;border-bottom-right-radius:4px}.toast-item.toast-information:before{background:var(--tds-information)}.toast-item.toast-success:before{background:var(--tds-positive)}.toast-item.toast-warning:before{background:var(--tds-warning)}.toast-item.toast-error:before{background:var(--tds-negative)}.toast-item .toast-close{height:20px;width:20px;box-sizing:border-box;cursor:pointer;position:absolute;top:14px;right:14px;border:0;background:transparent}.toast-item .toast-close:active{border:2px solid var(--tds-blue-400);outline-offset:-2px}@keyframes fadeIn{0%{transform:translateY(40%);opacity:0;margin-bottom:-25%}to{transform:translateY(0);opacity:1;margin-bottom:0%}}@keyframes fadeOut{0%{transform:translateY(0);opacity:1;margin-top:0}to{opacity:0;transform:translateY(75%);margin-top:-25%}}@keyframes progress{to{width:0%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TegelModule }, { kind: "component", type: i1.TdsLink, selector: "tds-link", inputs: ["disabled", "standalone", "underline"] }, { kind: "component", type: i1.TdsToast, selector: "tds-toast", inputs: ["closable", "header", "hidden", "subheader", "tdsAriaLive", "tdsCloseAriaLabel", "toastId", "toastRole", "variant"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
29
29
|
}
|
|
30
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
30
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ToastComponent, decorators: [{
|
|
31
31
|
type: Component,
|
|
32
|
-
args: [{ selector: '
|
|
32
|
+
args: [{ selector: 'tae-toast', imports: [CommonModule, TegelModule, RouterLink], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ul class=\"toast-list\" aria-live=\"polite\" aria-atomic=\"true\">\n @for (toast of toastsSignal(); track toast.id) {\n <li\n class=\"toast-item\"\n [class]=\"'toast-' + toast.type\"\n [class.toast-closing]=\"toast.state === 'closing'\"\n [style.--duration.ms]=\"toast.duration\"\n [style.--close-duration.ms]=\"toast.closeDuration\"\n role=\"status\"\n >\n <tds-toast\n [variant]=\"toast.type\"\n [header]=\"toast.title\"\n [subheader]=\"toast.description\"\n [closable]=\"toast.closable\"\n tds-close-aria-label=\"Toast close button\"\n >\n @if (toast.link) {\n <tds-link slot=\"actions\">\n <a [routerLink]=\"toast.link\">\n {{ toast.linkText ?? 'Click here' }}\n </a>\n </tds-link>\n } @else if (toast.action) {\n <tds-link slot=\"actions\">\n <a href=\"#\" (click)=\"toast.action()\">\n {{ toast.linkText ?? 'Click here' }}\n </a>\n </tds-link>\n }\n </tds-toast>\n @if (toast.closable) {\n <button\n class=\"toast-close\"\n (click)=\"closeToast(toast)\"\n aria-label=\"Close toast\"\n ></button>\n }\n </li>\n }\n</ul>\n", styles: [":host{position:fixed;right:0;bottom:0;overflow:hidden;z-index:9999}.toast-list{display:grid;grid-template-columns:1fr;gap:6px;list-style:none;padding:6px;margin:0}.toast-item{position:relative;animation:fadeIn .3s ease-in forwards}.toast-item.toast-closing{animation-name:fadeOut;animation-duration:var(--close-duration, .3s);animation-fill-mode:forwards;pointer-events:none}.toast-item:before{content:\"\";position:absolute;height:3px;width:100%;bottom:0;left:4px;right:0;animation:progress var(--duration, 7s) linear forwards;border-top-right-radius:4px;border-bottom-right-radius:4px}.toast-item.toast-information:before{background:var(--tds-information)}.toast-item.toast-success:before{background:var(--tds-positive)}.toast-item.toast-warning:before{background:var(--tds-warning)}.toast-item.toast-error:before{background:var(--tds-negative)}.toast-item .toast-close{height:20px;width:20px;box-sizing:border-box;cursor:pointer;position:absolute;top:14px;right:14px;border:0;background:transparent}.toast-item .toast-close:active{border:2px solid var(--tds-blue-400);outline-offset:-2px}@keyframes fadeIn{0%{transform:translateY(40%);opacity:0;margin-bottom:-25%}to{transform:translateY(0);opacity:1;margin-bottom:0%}}@keyframes fadeOut{0%{transform:translateY(0);opacity:1;margin-top:0}to{opacity:0;transform:translateY(75%);margin-top:-25%}}@keyframes progress{to{width:0%}}\n"] }]
|
|
33
33
|
}] });
|
|
34
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9hc3QuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy90ZWdlbC1hbmd1bGFyLWV4dGVuc2lvbnMvc3JjL2xpYi90b2FzdC90b2FzdC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9saWJzL3RlZ2VsLWFuZ3VsYXItZXh0ZW5zaW9ucy9zcmMvbGliL3RvYXN0L3RvYXN0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDN0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBR3ZELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQzs7O0FBRS9DOzs7OztHQUtHO0FBUUgsTUFBTSxPQUFPLGNBQWM7SUFDUixZQUFZLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBRXJEOztPQUVHO0lBQ00sWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDO0lBRXZEOzs7OztPQUtHO0lBQ0gsVUFBVSxHQUFHLENBQUMsS0FBWSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7d0dBZHRELGNBQWM7NEZBQWQsY0FBYyxxRUNyQjNCLDBxQ0F5Q0EsKzVDRHZCWSxZQUFZLDhCQUFFLFdBQVcsOFVBQUUsVUFBVTs7NEZBR3BDLGNBQWM7a0JBUDFCLFNBQVM7K0JBQ0UsV0FBVyxXQUdaLENBQUMsWUFBWSxFQUFFLFdBQVcsRUFBRSxVQUFVLENBQUMsbUJBQy9CLHVCQUF1QixDQUFDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBSb3V0ZXJMaW5rIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7IFRlZ2VsTW9kdWxlIH0gZnJvbSAnQHNjYW5pYS90ZWdlbC1hbmd1bGFyLTE3JztcblxuaW1wb3J0IHsgVG9hc3QgfSBmcm9tICcuL21vZGVscy90b2FzdC5tb2RlbCc7XG5pbXBvcnQgeyBUb2FzdFNlcnZpY2UgfSBmcm9tICcuL3RvYXN0LnNlcnZpY2UnO1xuXG4vKipcbiAqIERpc3BsYXlzIHRvYXN0IG5vdGlmaWNhdGlvbnMgcHJvdmlkZWQgYnkgdGhlIFRvYXN0U2VydmljZS5cbiAqXG4gKiBUb2FzdHMgYXJlIG5vbi1ibG9ja2luZyBtZXNzYWdlcyB0aGF0IGF1dG9tYXRpY2FsbHkgZGlzYXBwZWFyIGFmdGVyIGEgc2V0IGR1cmF0aW9uXG4gKiBvciBjYW4gYmUgZGlzbWlzc2VkIG1hbnVhbGx5IGJ5IHRoZSB1c2VyLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd0YWUtdG9hc3QnLFxuICB0ZW1wbGF0ZVVybDogJy4vdG9hc3QuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi90b2FzdC5jb21wb25lbnQuc2NzcyddLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlLCBUZWdlbE1vZHVsZSwgUm91dGVyTGlua10sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBUb2FzdENvbXBvbmVudCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgdG9hc3RTZXJ2aWNlID0gaW5qZWN0KFRvYXN0U2VydmljZSk7XG5cbiAgLyoqXG4gICAqIEEgcmVhY3RpdmUgc2lnbmFsIG9mIGFsbCBjdXJyZW50bHkgYWN0aXZlIHRvYXN0cyAob3BlbiBvciBjbG9zaW5nKS5cbiAgICovXG4gIHJlYWRvbmx5IHRvYXN0c1NpZ25hbCA9IHRoaXMudG9hc3RTZXJ2aWNlLmFjdGl2ZVRvYXN0cztcblxuICAvKipcbiAgICogSW5pdGlhdGVzIHRoZSBjbG9zaW5nIHByb2Nlc3MgZm9yIHRoZSBnaXZlbiB0b2FzdC5cbiAgICpcbiAgICogQHBhcmFtIHRvYXN0IFRoZSB0b2FzdCB0byBiZSBjbG9zZWQuXG4gICAqIEByZXR1cm5zIHZvaWRcbiAgICovXG4gIGNsb3NlVG9hc3QgPSAodG9hc3Q6IFRvYXN0KSA9PiB0aGlzLnRvYXN0U2VydmljZS5jbG9zZSh0b2FzdC5pZCk7XG59XG4iLCI8dWwgY2xhc3M9XCJ0b2FzdC1saXN0XCIgYXJpYS1saXZlPVwicG9saXRlXCIgYXJpYS1hdG9taWM9XCJ0cnVlXCI+XG4gIEBmb3IgKHRvYXN0IG9mIHRvYXN0c1NpZ25hbCgpOyB0cmFjayB0b2FzdC5pZCkge1xuICA8bGlcbiAgICBjbGFzcz1cInRvYXN0LWl0ZW1cIlxuICAgIFtjbGFzc109XCIndG9hc3QtJyArIHRvYXN0LnR5cGVcIlxuICAgIFtjbGFzcy50b2FzdC1jbG9zaW5nXT1cInRvYXN0LnN0YXRlID09PSAnY2xvc2luZydcIlxuICAgIFtzdHlsZS4tLWR1cmF0aW9uLm1zXT1cInRvYXN0LmR1cmF0aW9uXCJcbiAgICBbc3R5bGUuLS1jbG9zZS1kdXJhdGlvbi5tc109XCJ0b2FzdC5jbG9zZUR1cmF0aW9uXCJcbiAgICByb2xlPVwic3RhdHVzXCJcbiAgPlxuICAgIDx0ZHMtdG9hc3RcbiAgICAgIFt2YXJpYW50XT1cInRvYXN0LnR5cGVcIlxuICAgICAgW2hlYWRlcl09XCJ0b2FzdC50aXRsZVwiXG4gICAgICBbc3ViaGVhZGVyXT1cInRvYXN0LmRlc2NyaXB0aW9uXCJcbiAgICAgIFtjbG9zYWJsZV09XCJ0b2FzdC5jbG9zYWJsZVwiXG4gICAgICB0ZHMtY2xvc2UtYXJpYS1sYWJlbD1cIlRvYXN0IGNsb3NlIGJ1dHRvblwiXG4gICAgPlxuICAgICAgQGlmICh0b2FzdC5saW5rKSB7XG4gICAgICA8dGRzLWxpbmsgc2xvdD1cImFjdGlvbnNcIj5cbiAgICAgICAgPGEgW3JvdXRlckxpbmtdPVwidG9hc3QubGlua1wiPlxuICAgICAgICAgIHt7IHRvYXN0LmxpbmtUZXh0ID8/ICdDbGljayBoZXJlJyB9fVxuICAgICAgICA8L2E+XG4gICAgICA8L3Rkcy1saW5rPlxuICAgICAgfSBAZWxzZSBpZiAodG9hc3QuYWN0aW9uKSB7XG4gICAgICA8dGRzLWxpbmsgc2xvdD1cImFjdGlvbnNcIj5cbiAgICAgICAgPGEgaHJlZj1cIiNcIiAoY2xpY2spPVwidG9hc3QuYWN0aW9uKClcIj5cbiAgICAgICAgICB7eyB0b2FzdC5saW5rVGV4dCA/PyAnQ2xpY2sgaGVyZScgfX1cbiAgICAgICAgPC9hPlxuICAgICAgPC90ZHMtbGluaz5cbiAgICAgIH1cbiAgICA8L3Rkcy10b2FzdD5cbiAgICBAaWYgKHRvYXN0LmNsb3NhYmxlKSB7XG4gICAgPGJ1dHRvblxuICAgICAgY2xhc3M9XCJ0b2FzdC1jbG9zZVwiXG4gICAgICAoY2xpY2spPVwiY2xvc2VUb2FzdCh0b2FzdClcIlxuICAgICAgYXJpYS1sYWJlbD1cIkNsb3NlIHRvYXN0XCJcbiAgICA+PC9idXR0b24+XG4gICAgfVxuICA8L2xpPlxuICB9XG48L3VsPlxuIl19
|
|
@@ -194,10 +194,10 @@ export class ToastService {
|
|
|
194
194
|
const num = typeof value === 'number' ? value : fallback;
|
|
195
195
|
return !Number.isFinite(num) || num < 0 ? 0 : num;
|
|
196
196
|
}
|
|
197
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
198
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.
|
|
197
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
198
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ToastService, providedIn: 'root' });
|
|
199
199
|
}
|
|
200
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
200
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ToastService, decorators: [{
|
|
201
201
|
type: Injectable,
|
|
202
202
|
args: [{
|
|
203
203
|
providedIn: 'root',
|
package/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as _angular_core from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, EnvironmentProviders } from '@angular/core';
|
|
3
3
|
import z$1, { z } from 'zod';
|
|
4
4
|
import { zx } from '@traversable/zod';
|
|
5
|
+
import { Subject } from 'rxjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* List of available toast types (inherited from Tegel)
|
|
@@ -463,5 +464,134 @@ type ProvideStaticConfigOptions<T> = {
|
|
|
463
464
|
*/
|
|
464
465
|
declare function provideStaticConfig<T>(env: T | undefined, options: ProvideStaticConfigOptions<T>): EnvironmentProviders;
|
|
465
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
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Directive that triggers a hard page refresh when the host element is clicked
|
|
519
|
+
* a specified number of times within a configurable time window.
|
|
520
|
+
*
|
|
521
|
+
* This is useful for adding developer-oriented shortcuts, diagnostics entry points,
|
|
522
|
+
* hidden "triple-click to reload" behaviors, or fallback mechanisms in production.
|
|
523
|
+
*
|
|
524
|
+
* Usage:
|
|
525
|
+
* ```html
|
|
526
|
+
* <div taeHardRefresh [clicksRequired]="3" [clickWindowMs]="500">...</div>
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
declare class HardRefreshDirective {
|
|
530
|
+
/**
|
|
531
|
+
* The number of rapid consecutive clicks required to trigger a hard refresh.
|
|
532
|
+
* A "rapid" click is one that occurs within `clickWindowMs` of the previous click.
|
|
533
|
+
*
|
|
534
|
+
* @default 3
|
|
535
|
+
*/
|
|
536
|
+
readonly clicksRequired: _angular_core.InputSignal<number>;
|
|
537
|
+
/**
|
|
538
|
+
* The maximum time interval (in milliseconds) allowed between consecutive clicks
|
|
539
|
+
* for them to be considered part of the same "streak".
|
|
540
|
+
*
|
|
541
|
+
* If the interval between two clicks exceeds this value, the streak resets.
|
|
542
|
+
*
|
|
543
|
+
* @default 500
|
|
544
|
+
*/
|
|
545
|
+
readonly clickWindowMs: _angular_core.InputSignal<number>;
|
|
546
|
+
/**
|
|
547
|
+
* Internal click event stream used to build the rapid-click detection pipeline.
|
|
548
|
+
* Emits one value per host click (via the `(click)` host listener).
|
|
549
|
+
*/
|
|
550
|
+
protected readonly clicks$: Subject<void>;
|
|
551
|
+
constructor();
|
|
552
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HardRefreshDirective, never>;
|
|
553
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<HardRefreshDirective, "[taeHardRefresh]", never, { "clicksRequired": { "alias": "clicksRequired"; "required": false; "isSignal": true; }; "clickWindowMs": { "alias": "clickWindowMs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* An enhanced standalone footer component based on the Tegel `TdsFooterComponent`.
|
|
558
|
+
*
|
|
559
|
+
* `TaeFooterComponent` maintains the same visual style as the original
|
|
560
|
+
* Tegel footer, but adds two key improvements:
|
|
561
|
+
*
|
|
562
|
+
* - A **compact “small” variant** for layouts where vertical space is limited.
|
|
563
|
+
* - Optional **version display**, allowing applications to show their
|
|
564
|
+
* build or release version directly in the footer.
|
|
565
|
+
*
|
|
566
|
+
* Example:
|
|
567
|
+
* ```html
|
|
568
|
+
* <tae-footer variant="small" version="v1.0.0" />
|
|
569
|
+
* ```
|
|
570
|
+
*/
|
|
571
|
+
declare class TaeFooterComponent {
|
|
572
|
+
/**
|
|
573
|
+
* Determines the visual layout of the footer.
|
|
574
|
+
*
|
|
575
|
+
* - `"normal"` displays the standard footer layout (aligned to tds-footer)
|
|
576
|
+
* - `"small"` produces a more compact version suitable for tight layouts
|
|
577
|
+
*
|
|
578
|
+
* @default 'normal'
|
|
579
|
+
*/
|
|
580
|
+
readonly variant: _angular_core.InputSignal<"normal" | "small">;
|
|
581
|
+
/**
|
|
582
|
+
* Optional application version string to display in the footer.
|
|
583
|
+
*
|
|
584
|
+
* When provided, it appears left of the Scania logo.
|
|
585
|
+
* If omitted or `undefined`, the version section is not shown.
|
|
586
|
+
*/
|
|
587
|
+
readonly version: _angular_core.InputSignal<string | undefined>;
|
|
588
|
+
/**
|
|
589
|
+
* The current year used in the copyright section.
|
|
590
|
+
*/
|
|
591
|
+
readonly currentYear: number;
|
|
592
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TaeFooterComponent, never>;
|
|
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>;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export { BarcodeScannerDirective, DEFAULT_TOAST_CONFIG, HardRefreshDirective, TOAST_CONFIG, TaeFooterComponent, ToastService, createEnvKit, parseEnvFile, provideRuntimeConfig, provideStaticConfig, provideToast };
|
|
467
597
|
export type { Toast, ToastConfig, ToastOptions };
|