ngx-kit 8.4.1 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-kit",
3
- "version": "8.4.1",
3
+ "version": "9.0.0",
4
4
  "description": "Angular input components",
5
5
  "author": "Mohammadreza Samani <mr.samani1368@gmail.com>",
6
6
  "license": "MIT",
@@ -13,7 +13,43 @@
13
13
  "url": "https://github.com/mr-samani/ngx-kit/issues"
14
14
  },
15
15
  "keywords": [
16
- "angular"
16
+ "angular",
17
+ "i18n",
18
+ "translate",
19
+ "signals",
20
+ "lazy",
21
+ "scopes",
22
+ "input-color",
23
+ "color-picker",
24
+ "gradient-picker",
25
+ "select-angle",
26
+ "date-picker",
27
+ "calendar",
28
+ "file",
29
+ "drop-zone",
30
+ "image-viewer",
31
+ "draggable",
32
+ "resizable",
33
+ "kanban-view",
34
+ "drag-drop",
35
+ "shadow",
36
+ "message",
37
+ "notify",
38
+ "alert",
39
+ "dialog",
40
+ "modal",
41
+ "time-picker",
42
+ "tooltip",
43
+ "data-table",
44
+ "table",
45
+ "pagination",
46
+ "drop-zone",
47
+ "image-editor",
48
+ "media-control",
49
+ "media-player",
50
+ "audio-player",
51
+ "infinit-scroll",
52
+ "virtual-scroll"
17
53
  ],
18
54
  "peerDependencies": {
19
55
  "@angular/common": ">=22.0.0",
@@ -23,6 +59,9 @@
23
59
  "tslib": "^2.3.0"
24
60
  },
25
61
  "sideEffects": false,
62
+ "bin": {
63
+ "ngx-lazy-i18n-extract": "./lazy-i18n/bin/extract.mjs"
64
+ },
26
65
  "module": "fesm2022/ngx-kit.mjs",
27
66
  "typings": "types/ngx-kit.d.ts",
28
67
  "exports": {
@@ -93,6 +132,10 @@
93
132
  "types": "./types/ngx-kit-infinite-scroll.d.ts",
94
133
  "default": "./fesm2022/ngx-kit-infinite-scroll.mjs"
95
134
  },
135
+ "./lazy-i18n": {
136
+ "types": "./types/ngx-kit-lazy-i18n.d.ts",
137
+ "default": "./fesm2022/ngx-kit-lazy-i18n.mjs"
138
+ },
96
139
  "./media-control": {
97
140
  "types": "./types/ngx-kit-media-control.d.ts",
98
141
  "default": "./fesm2022/ngx-kit-media-control.mjs"
@@ -390,8 +390,6 @@ interface BaseOverlayOptions<T = any> {
390
390
  autoFocus?: boolean;
391
391
  /** Restore focus to the previously focused element when the overlay closes. Default: true. */
392
392
  restoreFocus?: boolean;
393
- /** Prevent the page behind the overlay from scrolling while it's open. Default: false. */
394
- lockBodyScroll?: boolean;
395
393
  /** Extra class(es) applied to the backdrop element. */
396
394
  backdropClass?: string | string[];
397
395
  /** Extra class(es) applied to the overlay panel element. */
@@ -436,7 +434,6 @@ declare class OverlayService {
436
434
  private readonly overlayStack;
437
435
  private globalListenersAttached;
438
436
  private layoutRaf;
439
- private bodyScrollLockCount;
440
437
  private previousBodyOverflow;
441
438
  private previousBodyPaddingRight;
442
439
  private get isBrowser();
@@ -449,23 +446,20 @@ declare class OverlayService {
449
446
  private createTemplateInstance;
450
447
  private pushInstance;
451
448
  private createHostElement;
452
- private createBackdropElement;
453
449
  private createOverlayElement;
454
450
  private addClasses;
455
451
  private renderHost;
456
452
  private destroyInstance;
453
+ private observeOverlayResize;
457
454
  private resolveUsePopover;
458
455
  private showHostPopover;
459
456
  private hideHostPopover;
460
457
  private supportsPopover;
461
- private lockBodyScroll;
462
- private unlockBodyScroll;
463
458
  private attachGlobalListeners;
464
459
  private detachGlobalListeners;
465
460
  private readonly onDocumentKeydown;
466
461
  private readonly onDocumentClick;
467
462
  private readonly onDocumentContextMenu;
468
- private readonly onDocumentScroll;
469
463
  private readonly onWindowResize;
470
464
  private scheduleReposition;
471
465
  private scheduleInitialLayout;
@@ -1,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { OnInit, OnDestroy, Injector, EnvironmentProviders, EnvironmentInjector, Type, InjectionToken } from '@angular/core';
2
+ import { OnInit, OnDestroy, Injector, EnvironmentProviders, EnvironmentInjector, InjectionToken, Type } from '@angular/core';
3
3
  import * as ngx_kit_dialog from 'ngx-kit/dialog';
4
4
  import { Observable } from 'rxjs';
5
5
  import { OverlayRef } from 'ngx-kit/core';
@@ -73,8 +73,6 @@ declare class NgxDialogConfig<DataType = any> {
73
73
  * dialog - return `false` to block it (e.g. unsaved changes). Does not
74
74
  * affect a programmatic `dialogRef.close()` call. */
75
75
  beforeClose?: () => boolean;
76
- /** Prevent the page behind the dialog from scrolling while it's open. Default: true. */
77
- lockBodyScroll?: boolean;
78
76
  /** Move focus into the dialog on open. Default: true. */
79
77
  autoFocus?: boolean;
80
78
  /** Restore focus to the previously focused element on close. Default: true. */
@@ -169,24 +167,11 @@ declare class NgxDialogHeader implements OnInit {
169
167
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgxDialogHeader, "ngx-dialog-header,[ngxDialogHeader]", ["ngxDialogHeader"], { "showCloseButton": { "alias": "showCloseButton"; "required": false; "isSignal": true; }; "showMaximizeButton": { "alias": "showMaximizeButton"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.NgxDraggable; inputs: {}; outputs: {}; }]>;
170
168
  }
171
169
 
172
- /**
173
- * Why this can't be pure CSS flexbox: the header/body/footer elements are
174
- * *grandchildren* of the dialog panel (panel -> `*ngComponentOutlet`'s host
175
- * element -> header/body/footer), not direct children, because the content
176
- * component that hosts them is arbitrary and we don't control its own host
177
- * styling. `display:flex;flex-direction:column` on the panel therefore can't
178
- * size the body against the header/footer through that extra layer, so we
179
- * measure and set an explicit `max-height` instead, driven by a
180
- * `ResizeObserver` on the panel (not a `window:resize` listener, so it also
181
- * reacts to the dialog itself changing size, e.g. via `config.size`).
182
- */
183
170
  declare class NgxDialogBody {
184
171
  private readonly el;
185
- private readonly renderer;
186
172
  private readonly dialogRef;
187
173
  private readonly destroyRef;
188
174
  constructor();
189
- private recomputeHeight;
190
175
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgxDialogBody, never>;
191
176
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgxDialogBody, "ngx-dialog-body,[ngxDialogBody]", ["ngxDialogBody"], {}, {}, never, never, true, never>;
192
177
  }
@@ -197,6 +182,13 @@ declare class NgxDialogModule {
197
182
  static ɵinj: _angular_core.ɵɵInjectorDeclaration<NgxDialogModule>;
198
183
  }
199
184
 
185
+ declare const DIALOG_DATA: InjectionToken<any>;
186
+ declare const DIALOG_REF: InjectionToken<NgxDialogRef<any>>;
187
+ /** The resolved config for the dialog currently being rendered. */
188
+ declare const NGX_DIALOG_CONFIG: InjectionToken<NgxDialogConfig<any>>;
189
+ /** The arbitrary content component passed to `open()`, projected via `*ngComponentOutlet`. */
190
+ declare const DIALOG_CONTENT: InjectionToken<Type<any>>;
191
+
200
192
  declare class NgxDialogService {
201
193
  private readonly overlay;
202
194
  private readonly injector;
@@ -219,30 +211,5 @@ declare class NgxDialogService {
219
211
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<NgxDialogService>;
220
212
  }
221
213
 
222
- /**
223
- * Static convenience API over `NgxDialogService`, so you can call
224
- * `Dialog.open(...)` from anywhere without injecting the service manually.
225
- *
226
- * SSR note: the instance backing this static class is process-wide, not
227
- * per-request, so it is deliberately left unset on the server (see
228
- * `provideNgxDialog()`). Inject `NgxDialogService` directly in SSR code paths.
229
- */
230
- declare class Dialog {
231
- private static serviceInstance;
232
- private static readonly openRefs;
233
- /** @internal set by `provideNgxDialog()`'s environment initializer. */
234
- static _setService(svc: NgxDialogService): void;
235
- private static get service();
236
- static open<R = any, DataType = any>(component: Type<any>, config?: Partial<NgxDialogConfig<DataType>>): NgxDialogRef<R>;
237
- static closeAll(): void;
238
- }
239
-
240
- declare const DIALOG_DATA: InjectionToken<any>;
241
- declare const DIALOG_REF: InjectionToken<NgxDialogRef<any>>;
242
- /** The resolved config for the dialog currently being rendered. */
243
- declare const NGX_DIALOG_CONFIG: InjectionToken<NgxDialogConfig<any>>;
244
- /** The arbitrary content component passed to `open()`, projected via `*ngComponentOutlet`. */
245
- declare const DIALOG_CONTENT: InjectionToken<Type<any>>;
246
-
247
- export { DIALOG_CONTENT, DIALOG_DATA, DIALOG_REF, Dialog, NGX_DIALOG_CONFIG, NgxDialogBody, NgxDialogComponent, NgxDialogConfig, NgxDialogFooter, NgxDialogHeader, NgxDialogModule, NgxDialogService, provideNgxDialog };
214
+ export { DIALOG_CONTENT, DIALOG_DATA, DIALOG_REF, NGX_DIALOG_CONFIG, NgxDialogBody, NgxDialogComponent, NgxDialogConfig, NgxDialogFooter, NgxDialogHeader, NgxDialogModule, NgxDialogService, provideNgxDialog };
248
215
  export type { NgxDialogFooterAlign, NgxDialogFooterConfig, NgxDialogHeaderConfig, NgxDialogSize };
@@ -0,0 +1,213 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Type, PipeTransform, Signal, EnvironmentProviders } from '@angular/core';
3
+ import { Observable } from 'rxjs';
4
+ import { CanActivateChildFn } from '@angular/router';
5
+
6
+ /** A (possibly nested) translation file as returned by the server. */
7
+ interface Dictionary {
8
+ [key: string]: string | number | boolean | null | Dictionary | Array<unknown>;
9
+ }
10
+ type Params = Record<string, unknown>;
11
+ type TranslateFn = (key: string, params?: Params) => string;
12
+ type MissingHandler = (key: string, lang: string) => string;
13
+ type Transpiler = (value: string, params: Params | undefined, lang: string) => string;
14
+ interface I18nLoader {
15
+ /** `scope === undefined` means the global file. */
16
+ load(lang: string, scope?: string): Observable<Dictionary> | Promise<Dictionary>;
17
+ }
18
+ declare class I18nConfig {
19
+ /** All supported languages. */
20
+ langs: string[];
21
+ defaultLang: string;
22
+ /** Looked up (in order) when a key is empty/missing in the active language. */
23
+ fallbackLangs?: string[];
24
+ /** Default `'key'` (returns the key itself). */
25
+ missing?: 'key' | 'empty' | MissingHandler;
26
+ /** Log each missing key once. Default: `isDevMode()`. */
27
+ warnMissing?: boolean;
28
+ /** Replace the default `{{ name }}` interpolation (ICU, plurals, ...). */
29
+ transpile?: Transpiler;
30
+ /** Custom loader class (must be `@Injectable({providedIn:'root'})` or constructible without args). Default: HTTP. */
31
+ loader?: Type<I18nLoader>;
32
+ /** Default HTTP loader: base URL. Default `'/i18n'` -> `/i18n/fa`, `/i18n/setting/fa`. */
33
+ baseUrl?: string;
34
+ /** Default HTTP loader: file extension appended to the URL, e.g. `'.json'`. Default `'.json'`. */
35
+ fileExtension?: string;
36
+ /** Default HTTP loader: full control over the URL. */
37
+ url?: (lang: string, scope?: string) => string;
38
+ /** Retries per failed request. Default `1`. */
39
+ retries?: number;
40
+ /** localStorage key used to remember the language. `null` disables. Default `'lazy-i18n.lang'`. */
41
+ storageKey?: string | null;
42
+ /** Pick the initial language from `navigator.languages` when nothing is stored. Default `false`. */
43
+ detectBrowserLang?: boolean;
44
+ /** Keep `<html lang dir>` in sync. Default `true`. */
45
+ manageDocument?: boolean;
46
+ }
47
+ declare const I18N_CONFIG: InjectionToken<I18nConfig>;
48
+ declare const I18N_LOADER: InjectionToken<I18nLoader>;
49
+
50
+ /**
51
+ * `{{ 'orders.title' | tr }}` / `{{ 'hello' | tr: { name } }}`
52
+ *
53
+ * Impure on purpose (it must follow signals), but memoised: when key, params and the
54
+ * service revision are unchanged it returns the cached string in O(1) with no lookup.
55
+ */
56
+ declare class TrPipe implements PipeTransform {
57
+ private readonly svc;
58
+ private k?;
59
+ private p?;
60
+ private rev;
61
+ private out;
62
+ transform(key: string | null | undefined, params?: Params): string;
63
+ static ɵfac: i0.ɵɵFactoryDeclaration<TrPipe, never>;
64
+ static ɵpipe: i0.ɵɵPipeDeclaration<TrPipe, "tr", true>;
65
+ }
66
+
67
+ interface TrContext {
68
+ $implicit: TranslateFn;
69
+ t: TranslateFn;
70
+ lang: Signal<string>;
71
+ }
72
+ /**
73
+ * Structural directive. The view is created ONCE; `t` reads signals, so only the bindings that use it update.
74
+ *
75
+ * <ng-container *tr="let t; prefix: 'orders'">
76
+ * <h1>{{ t('title') }}</h1> <!-- orders.title -->
77
+ * </ng-container>
78
+ */
79
+ declare class TrDirective {
80
+ private readonly tpl;
81
+ private readonly vcr;
82
+ private readonly svc;
83
+ private readonly prefix;
84
+ /** `prefix: 'orders'` in the microsyntax. */
85
+ set trPrefix(v: string | undefined);
86
+ static ngTemplateContextGuard(_dir: TrDirective, _ctx: unknown): _ctx is TrContext;
87
+ constructor();
88
+ static ɵfac: i0.ɵɵFactoryDeclaration<TrDirective, never>;
89
+ static ɵdir: i0.ɵɵDirectiveDeclaration<TrDirective, "[tr],[translate]", never, { "trPrefix": { "alias": "trPrefix"; "required": false; }; }, {}, never, never, true, never>;
90
+ }
91
+ /**
92
+ * Attribute directive.
93
+ * <span trKey="orders.title"></span>
94
+ * <input trKey="form.name" trAttr="placeholder" />
95
+ * <p trKey="hello" [trParams]="{ name: user.name }"></p>
96
+ */
97
+ declare class TrKeyDirective {
98
+ private readonly key;
99
+ private readonly params;
100
+ private readonly attr;
101
+ private readonly prefix;
102
+ set trKey(v: string | undefined);
103
+ set trParams(v: Params | undefined);
104
+ set trAttr(v: string | undefined);
105
+ set trPrefix(v: string | undefined);
106
+ constructor();
107
+ static ɵfac: i0.ɵɵFactoryDeclaration<TrKeyDirective, never>;
108
+ static ɵdir: i0.ɵɵDirectiveDeclaration<TrKeyDirective, "[trKey]", never, { "trKey": { "alias": "trKey"; "required": false; }; "trParams": { "alias": "trParams"; "required": false; }; "trAttr": { "alias": "trAttr"; "required": false; }; "trPrefix": { "alias": "trPrefix"; "required": false; }; }, {}, never, never, true, never>;
109
+ }
110
+
111
+ /** Flatten a nested dictionary into `target` (dot-separated keys). Non-string leaves are stringified. */
112
+ declare function flatten(dict: Dictionary, target?: Map<string, string>, onConflict?: (key: string) => void, prefix?: string): Map<string, string>;
113
+ /** `Hello {{ name }}` + `{name:'Ali'}` -> `Hello Ali`. Unknown placeholders are left untouched. */
114
+ declare function interpolate(text: string, params?: Params): string;
115
+ /** Identity function the extractor recognises: `const k = marker('orders.title')`. */
116
+ declare function marker<T extends string>(key: T): T;
117
+
118
+ /** Default loader: GET `{baseUrl}/{lang}` and `{baseUrl}/{scope}/{lang}`. Goes through your HttpClient interceptors. */
119
+ declare class HttpI18nLoader implements I18nLoader {
120
+ private readonly http;
121
+ private readonly cfg;
122
+ load(lang: string, scope?: string): Observable<Dictionary>;
123
+ static ɵfac: i0.ɵɵFactoryDeclaration<HttpI18nLoader, never>;
124
+ static ɵprov: i0.ɵɵInjectableDeclaration<HttpI18nLoader>;
125
+ }
126
+
127
+ declare class I18nService {
128
+ private readonly cfg;
129
+ private readonly loader;
130
+ private readonly doc;
131
+ private readonly _lang;
132
+ private readonly _rev;
133
+ /** Active language (reactive). */
134
+ readonly lang: Signal<string>;
135
+ /** Bumps whenever the active language or any dictionary changes. */
136
+ readonly revision: Signal<number>;
137
+ readonly dir: Signal<"rtl" | "ltr">;
138
+ readonly langs: readonly string[];
139
+ private readonly fallbacks;
140
+ private readonly warn;
141
+ private readonly dicts;
142
+ private readonly loaded;
143
+ private readonly inflight;
144
+ private readonly scopes;
145
+ private readonly pending;
146
+ private readonly warned;
147
+ private useToken;
148
+ /** Translate. Reads signals, so it is reactive inside `computed`, `effect` and templates. */
149
+ t(key: string, params?: Params): string;
150
+ /** Same as `t`, but never creates a reactive dependency. */
151
+ instant(key: string, params?: Params): string;
152
+ /** `computed` signal for one key. `params` may be a function reading other signals. */
153
+ select(key: string, params?: Params | (() => Params)): Signal<string>;
154
+ /** True when the key has a non-empty value in the active language or a fallback. */
155
+ has(key: string): boolean;
156
+ /** Returns every key below `prefix` as a nested object (values are raw, not interpolated). */
157
+ getObject(prefix: string): Dictionary;
158
+ private lookup;
159
+ private resolve;
160
+ private onMissing;
161
+ /**
162
+ * Load global + all registered scopes (and fallback languages) for `lang`, THEN activate it.
163
+ * Nothing renders half-translated and no "missing" flash occurs.
164
+ */
165
+ use(lang: string): Promise<void>;
166
+ /** Download a language without activating it. */
167
+ preload(lang: string): Promise<void>;
168
+ /** Language to start with: stored > browser (opt-in) > default. */
169
+ detectInitialLang(): string;
170
+ /** Register a scope and start loading it for the active language. Idempotent. Used by `provideI18nScope`. */
171
+ registerScope(scope: string): void;
172
+ /** Resolves once every scope registered so far is available. Never rejects. */
173
+ whenReady(): Promise<void>;
174
+ /** Manually set translations (tests, SSR transfer state, ...). */
175
+ setTranslation(lang: string, dict: Dictionary, merge?: boolean): void;
176
+ private merge;
177
+ private fetch;
178
+ private loadLang;
179
+ private isComplete;
180
+ private storageKey;
181
+ private persist;
182
+ private applyDocument;
183
+ static ɵfac: i0.ɵɵFactoryDeclaration<I18nService, never>;
184
+ static ɵprov: i0.ɵɵInjectableDeclaration<I18nService>;
185
+ }
186
+
187
+ /**
188
+ * ```ts
189
+ * provideHttpClient(),
190
+ * provideI18n({ langs: ['fa', 'en'], defaultLang: 'fa', fallbackLangs: ['en'] })
191
+ * ```
192
+ * The global dictionary of the initial language is loaded before the app starts.
193
+ */
194
+ declare function provideI18n(config: I18nConfig): EnvironmentProviders;
195
+
196
+ /**
197
+ * Put in `route.providers`. Runs when the route injector is created (before guards),
198
+ * so loading starts as early as possible. Child routes need nothing.
199
+ */
200
+ declare function provideI18nScope(scope: string | string[]): EnvironmentProviders;
201
+ /** Put ONCE on the root path-less route: `canActivateChild: [i18nReadyGuard]`. Waits for every registered scope. */
202
+ declare const i18nReadyGuard: CanActivateChildFn;
203
+
204
+ /** `readonly title = translateSignal('orders.title')` (injection context) */
205
+ declare function translateSignal(key: string, params?: Params | (() => Params)): Signal<string>;
206
+ /** `const t = injectT(); computed(() => t('a.b'))` — reactive translate function. */
207
+ declare function injectT(): TranslateFn;
208
+
209
+ /** `imports: [TR]` brings the pipe and both directives into a standalone component. */
210
+ declare const TR: readonly [typeof TrPipe, typeof TrDirective, typeof TrKeyDirective];
211
+
212
+ export { HttpI18nLoader, I18N_CONFIG, I18N_LOADER, I18nConfig, I18nService, TR, TrDirective, TrKeyDirective, TrPipe, flatten, i18nReadyGuard, injectT, interpolate, marker, provideI18n, provideI18nScope, translateSignal };
213
+ export type { Dictionary, I18nLoader, MissingHandler, Params, TrContext, TranslateFn, Transpiler };
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { TemplateRef, OnDestroy, ElementRef, ApplicationRef } from '@angular/core';
2
+ import { TemplateRef, OnDestroy, ApplicationRef, ElementRef } from '@angular/core';
3
3
  import { OverlayRef, Placement, Alignment, OverlayService } from 'ngx-kit/core';
4
4
 
5
5
  interface MenuContext {
@@ -16,14 +16,14 @@ declare class NgxMenuPanel implements MenuContext {
16
16
  }
17
17
 
18
18
  declare class NgxMenu implements OnDestroy {
19
- private el;
20
- private appRef;
21
- private overlayService;
22
19
  ngxMenu: i0.InputSignal<NgxMenuPanel>;
23
20
  placement: i0.InputSignal<Placement | undefined>;
24
21
  alignment: i0.InputSignal<Alignment | undefined>;
25
22
  private containerRef?;
26
- constructor(el: ElementRef, appRef: ApplicationRef, overlayService: OverlayService);
23
+ protected readonly overlay: OverlayService;
24
+ protected readonly appRef: ApplicationRef;
25
+ protected readonly el: ElementRef<HTMLElement>;
26
+ constructor();
27
27
  ngOnDestroy(): void;
28
28
  onClick(ev: Event): void;
29
29
  private toggleContainer;
@@ -33,13 +33,13 @@ declare class NgxMenu implements OnDestroy {
33
33
  }
34
34
 
35
35
  declare class NgxContextMenu implements OnDestroy {
36
- private el;
37
- private appRef;
38
- private overlayService;
39
36
  ngxContextMenu: i0.InputSignal<NgxMenuPanel>;
40
37
  private containerRef?;
41
38
  private readonly boundOnClick;
42
- constructor(el: ElementRef<HTMLElement>, appRef: ApplicationRef, overlayService: OverlayService);
39
+ protected readonly overlay: OverlayService;
40
+ protected readonly appRef: ApplicationRef;
41
+ protected readonly el: ElementRef<HTMLElement>;
42
+ constructor();
43
43
  ngOnDestroy(): void;
44
44
  onClick(ev: PointerEvent): void;
45
45
  private toggleContainer;