@ngneers/controls 0.0.1-pre1

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.
Files changed (40) hide show
  1. package/api/index.d.ts +134 -0
  2. package/custom-types/index.d.ts +7 -0
  3. package/dialog/index.d.ts +43 -0
  4. package/fesm2022/ngneers-controls-api.mjs +318 -0
  5. package/fesm2022/ngneers-controls-api.mjs.map +1 -0
  6. package/fesm2022/ngneers-controls-custom-types.mjs +4 -0
  7. package/fesm2022/ngneers-controls-custom-types.mjs.map +1 -0
  8. package/fesm2022/ngneers-controls-dialog.mjs +74 -0
  9. package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
  10. package/fesm2022/ngneers-controls-form-field.mjs +33 -0
  11. package/fesm2022/ngneers-controls-form-field.mjs.map +1 -0
  12. package/fesm2022/ngneers-controls-icon.mjs +53 -0
  13. package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
  14. package/fesm2022/ngneers-controls-lazy-cacher.mjs +30 -0
  15. package/fesm2022/ngneers-controls-lazy-cacher.mjs.map +1 -0
  16. package/fesm2022/ngneers-controls-list-box.mjs +119 -0
  17. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
  18. package/fesm2022/ngneers-controls-popover.mjs +80 -0
  19. package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
  20. package/fesm2022/ngneers-controls-scroller.mjs +163 -0
  21. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
  22. package/fesm2022/ngneers-controls-select.mjs +155 -0
  23. package/fesm2022/ngneers-controls-select.mjs.map +1 -0
  24. package/fesm2022/ngneers-controls-text-field.mjs +23 -0
  25. package/fesm2022/ngneers-controls-text-field.mjs.map +1 -0
  26. package/fesm2022/ngneers-controls-utils.mjs +104 -0
  27. package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
  28. package/fesm2022/ngneers-controls.mjs +6 -0
  29. package/fesm2022/ngneers-controls.mjs.map +1 -0
  30. package/form-field/index.d.ts +19 -0
  31. package/icon/index.d.ts +30 -0
  32. package/index.d.ts +2 -0
  33. package/lazy-cacher/index.d.ts +14 -0
  34. package/list-box/index.d.ts +58 -0
  35. package/package.json +78 -0
  36. package/popover/index.d.ts +41 -0
  37. package/scroller/index.d.ts +95 -0
  38. package/select/index.d.ts +86 -0
  39. package/text-field/index.d.ts +10 -0
  40. package/utils/index.d.ts +36 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-dialog.mjs","sources":["../../../packages/controls/src/dialog/dialog-templates.ts","../../../packages/controls/src/dialog/dialog.ts","../../../packages/controls/src/dialog/dialog.html","../../../packages/controls/src/dialog/ngneers-controls-dialog.ts"],"sourcesContent":["import { Component, computed, contentChild, input, TemplateRef, viewChild } from '@angular/core';\r\nimport { templateTypesFn } from '@ngneers/controls/api';\r\n\r\n@Component({\r\n template: '',\r\n})\r\nexport abstract class DialogTemplates {\r\n private readonly _defaultHeaderTemplate =\r\n viewChild.required<TemplateRef<unknown>>('defaultHeaderTemplate');\r\n private readonly _userHeaderTemplate = contentChild<TemplateRef<unknown>>('header');\r\n public readonly templateHeader = input<TemplateRef<unknown> | null>(null);\r\n protected readonly headerTemplate = computed(\r\n () => this._userHeaderTemplate() ?? this.templateHeader() ?? this._defaultHeaderTemplate()\r\n );\r\n\r\n private readonly _defaultFooterTemplate =\r\n viewChild.required<TemplateRef<unknown>>('defaultFooterTemplate');\r\n private readonly _userFooterTemplate = contentChild<TemplateRef<unknown>>('footer');\r\n public readonly templateFooter = input<TemplateRef<unknown> | null>(null);\r\n protected readonly footerTemplate = computed(\r\n () => this._userFooterTemplate() ?? this.templateFooter() ?? this._defaultFooterTemplate()\r\n );\r\n\r\n /**\r\n * Types for the dialog templates.\r\n */\r\n public readonly templateTypes = templateTypesFn<{\r\n header: {\r\n headerId: string;\r\n title: string | null;\r\n };\r\n }>();\r\n}\r\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n afterRenderEffect,\n ChangeDetectionStrategy,\n Component,\n contentChild,\n ElementRef,\n input,\n output,\n TemplateRef,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { NgnTemplate } from '@ngneers/controls/api';\nimport { LazyCacher } from '@ngneers/controls/lazy-cacher';\nimport { generateElementId } from '@ngneers/controls/utils';\n\nimport { DialogTemplates } from './dialog-templates';\n\n@Component({\n selector: 'ngn-dialog',\n imports: [NgTemplateOutlet, NgnTemplate, LazyCacher],\n templateUrl: './dialog.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class Dialog extends DialogTemplates {\n protected readonly headerId = generateElementId();\n\n private readonly _dialogElement = viewChild.required<ElementRef<HTMLDialogElement>>('dialog');\n protected readonly lazyContent = contentChild<TemplateRef<unknown>>('lazy');\n\n public readonly open = input(true);\n public readonly cache = input(false);\n public readonly modal = input(false);\n public readonly autofocus = input(this.modal());\n public readonly title = input<string | null>(null);\n public readonly closeOnEscape = input(true);\n\n public readonly closed = output<void>();\n\n constructor() {\n super();\n afterRenderEffect(() => {\n if (this.open()) {\n if (untracked(this.modal)) {\n this._dialogElement().nativeElement.showModal();\n } else {\n this._dialogElement().nativeElement.show();\n }\n } else {\n this._dialogElement().nativeElement.close();\n }\n });\n }\n\n protected onCancel() {\n this.closed.emit();\n }\n}\n","<!-- eslint-disable @angular-eslint/template/no-autofocus -->\n<dialog\n #dialog\n [attr.closedby]=\"closeOnEscape() ? 'closerequest' : 'none'\"\n [autofocus]=\"autofocus()\"\n [attr.aria-labelledby]=\"headerId\"\n (cancel)=\"onCancel()\"\n>\n <header>\n <ng-template\n [ngTemplateOutlet]=\"headerTemplate()\"\n [ngTemplateOutletContext]=\"{ headerId, title: title() }\"\n ></ng-template>\n </header>\n\n <ngn-lazy-cacher [lazyContent]=\"lazyContent()\" [open]=\"open()\" [cache]=\"cache()\">\n <ng-content />\n </ngn-lazy-cacher>\n\n <footer>\n <ng-template [ngTemplateOutlet]=\"footerTemplate()\"></ng-template>\n </footer>\n</dialog>\n\n<ng-template\n #defaultHeaderTemplate\n [ngnTemplate]=\"templateTypes.header\"\n let-headerId=\"headerId\"\n let-title=\"title\"\n>\n @if (title) {\n <h2 [id]=\"headerId\">{{ title }}</h2>\n }\n</ng-template>\n\n<ng-template #defaultFooterTemplate></ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, input, forwardRef, Component } from '@angular/core';
3
+
4
+ const FORM_FIELD = new InjectionToken('FormField');
5
+ class FormField {
6
+ _parentFormField = inject(FORM_FIELD, { optional: true, skipSelf: true });
7
+ hasParentFormField = !!this._parentFormField;
8
+ label = input(null);
9
+ inputId = input(null);
10
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FormField, deps: [], target: i0.ɵɵFactoryTarget.Component });
11
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.5", type: FormField, isStandalone: true, selector: "ngn-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
12
+ {
13
+ provide: FORM_FIELD,
14
+ useExisting: forwardRef(() => FormField),
15
+ },
16
+ ], ngImport: i0, template: "<div [class.ngn-form-field]=\"!hasParentFormField\">\r\n @if (label(); as label) {\r\n <label [for]=\"inputId()\">{{ label }}</label>\r\n }\r\n <ng-content />\r\n</div>\r\n", styles: [".ngn-form-field{border:2px solid black}\n"] });
17
+ }
18
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FormField, decorators: [{
19
+ type: Component,
20
+ args: [{ selector: 'ngn-form-field', providers: [
21
+ {
22
+ provide: FORM_FIELD,
23
+ useExisting: forwardRef(() => FormField),
24
+ },
25
+ ], template: "<div [class.ngn-form-field]=\"!hasParentFormField\">\r\n @if (label(); as label) {\r\n <label [for]=\"inputId()\">{{ label }}</label>\r\n }\r\n <ng-content />\r\n</div>\r\n", styles: [".ngn-form-field{border:2px solid black}\n"] }]
26
+ }] });
27
+
28
+ /**
29
+ * Generated bundle index. Do not edit.
30
+ */
31
+
32
+ export { FormField };
33
+ //# sourceMappingURL=ngneers-controls-form-field.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-form-field.mjs","sources":["../../../packages/controls/src/form-field/form-field.ts","../../../packages/controls/src/form-field/form-field.html","../../../packages/controls/src/form-field/ngneers-controls-form-field.ts"],"sourcesContent":["import { Component, forwardRef, inject, InjectionToken, input } from '@angular/core';\r\n\r\nimport { FormFieldBase } from './form-field-base';\r\n\r\nconst FORM_FIELD = new InjectionToken<FormField>('FormField');\r\n\r\n@Component({\r\n selector: 'ngn-form-field',\r\n templateUrl: './form-field.html',\r\n styles: `\r\n .ngn-form-field {\r\n border: 2px solid black;\r\n }\r\n `,\r\n providers: [\r\n {\r\n provide: FORM_FIELD,\r\n useExisting: forwardRef(() => FormField),\r\n },\r\n ],\r\n})\r\nexport class FormField implements FormFieldBase {\r\n private readonly _parentFormField = inject(FORM_FIELD, { optional: true, skipSelf: true });\r\n protected readonly hasParentFormField = !!this._parentFormField;\r\n\r\n public readonly label = input<string | null>(null);\r\n public readonly inputId = input<string | null>(null);\r\n}\r\n","<div [class.ngn-form-field]=\"!hasParentFormField\">\r\n @if (label(); as label) {\r\n <label [for]=\"inputId()\">{{ label }}</label>\r\n }\r\n <ng-content />\r\n</div>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAIA,MAAM,UAAU,GAAG,IAAI,cAAc,CAAY,WAAW,CAAC;MAiBhD,SAAS,CAAA;AACH,IAAA,gBAAgB,GAAG,MAAM,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACvE,IAAA,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB;AAE/C,IAAA,KAAK,GAAG,KAAK,CAAgB,IAAI,CAAC;AAClC,IAAA,OAAO,GAAG,KAAK,CAAgB,IAAI,CAAC;uGALzC,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAT,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,SAAS,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,SAAA,EAPT;AACT,YAAA;AACE,gBAAA,OAAO,EAAE,UAAU;AACnB,gBAAA,WAAW,EAAE,UAAU,CAAC,MAAM,SAAS,CAAC;AACzC,aAAA;AACF,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECnBH,oLAMA,EAAA,MAAA,EAAA,CAAA,2CAAA,CAAA,EAAA,CAAA;;2FDea,SAAS,EAAA,UAAA,EAAA,CAAA;kBAfrB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,EAAA,SAAA,EAOf;AACT,wBAAA;AACE,4BAAA,OAAO,EAAE,UAAU;AACnB,4BAAA,WAAW,EAAE,UAAU,CAAC,eAAe,CAAC;AACzC,yBAAA;AACF,qBAAA,EAAA,QAAA,EAAA,oLAAA,EAAA,MAAA,EAAA,CAAA,2CAAA,CAAA,EAAA;;;AEnBH;;AAEG;;;;"}
@@ -0,0 +1,53 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, Injectable, inject, input, viewChild, computed, afterRenderEffect, Component } from '@angular/core';
3
+ import { NgTemplateOutlet } from '@angular/common';
4
+
5
+ class GlobalIconTemplate {
6
+ _globalIconTemplate = signal(null);
7
+ globalIconTemplate = this._globalIconTemplate.asReadonly();
8
+ setGlobalIconTemplate(template) {
9
+ this._globalIconTemplate.set(template);
10
+ }
11
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: GlobalIconTemplate, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
12
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: GlobalIconTemplate, providedIn: 'root' });
13
+ }
14
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: GlobalIconTemplate, decorators: [{
15
+ type: Injectable,
16
+ args: [{ providedIn: 'root' }]
17
+ }] });
18
+
19
+ class Icon {
20
+ _globalIconTemplate = inject(GlobalIconTemplate).globalIconTemplate;
21
+ defaultIcon = input();
22
+ icon = input();
23
+ _defaultIconTemplate = viewChild.required('defaultIconTemplate');
24
+ usedIconTemplate = computed(() => {
25
+ const globalTemplate = this._globalIconTemplate();
26
+ if (globalTemplate) {
27
+ return globalTemplate;
28
+ }
29
+ return this._defaultIconTemplate();
30
+ });
31
+ constructor() {
32
+ afterRenderEffect(() => {
33
+ if (!this.icon() && !this.defaultIcon()) {
34
+ throw new Error('Icon component requires either an icon or a default icon to be set.');
35
+ }
36
+ });
37
+ }
38
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: Icon, deps: [], target: i0.ɵɵFactoryTarget.Component });
39
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.5", type: Icon, isStandalone: true, selector: "ngn-icon", inputs: { defaultIcon: { classPropertyName: "defaultIcon", publicName: "defaultIcon", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.ngSkipHydration": "true" } }, viewQueries: [{ propertyName: "_defaultIconTemplate", first: true, predicate: ["defaultIconTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (icon()) {\r\n <ng-container *ngTemplateOutlet=\"usedIconTemplate(); context: { $implicit: icon() }\">\r\n </ng-container>\r\n\r\n <ng-template #defaultIconTemplate let-icon><i class=\"{{ icon }}\"></i></ng-template>\r\n} @else {\r\n <i class=\"{{ defaultIcon() }}\"></i>\r\n}\r\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
40
+ }
41
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: Icon, decorators: [{
42
+ type: Component,
43
+ args: [{ selector: 'ngn-icon', imports: [NgTemplateOutlet], host: {
44
+ '[attr.ngSkipHydration]': 'true',
45
+ }, template: "@if (icon()) {\r\n <ng-container *ngTemplateOutlet=\"usedIconTemplate(); context: { $implicit: icon() }\">\r\n </ng-container>\r\n\r\n <ng-template #defaultIconTemplate let-icon><i class=\"{{ icon }}\"></i></ng-template>\r\n} @else {\r\n <i class=\"{{ defaultIcon() }}\"></i>\r\n}\r\n" }]
46
+ }], ctorParameters: () => [] });
47
+
48
+ /**
49
+ * Generated bundle index. Do not edit.
50
+ */
51
+
52
+ export { GlobalIconTemplate, Icon };
53
+ //# sourceMappingURL=ngneers-controls-icon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-icon.mjs","sources":["../../../packages/controls/src/icon/global-icon-template.ts","../../../packages/controls/src/icon/icon.ts","../../../packages/controls/src/icon/icon.html","../../../packages/controls/src/icon/ngneers-controls-icon.ts"],"sourcesContent":["import { Injectable, signal, TemplateRef } from '@angular/core';\r\nimport { IconType } from '@ngneers/controls/custom-types';\r\n\r\nexport type IconTemplateData = { $implicit: IconType };\r\nexport type IconTemplateType = TemplateRef<IconTemplateData>;\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class GlobalIconTemplate {\r\n private readonly _globalIconTemplate = signal<IconTemplateType | null>(null);\r\n\r\n public readonly globalIconTemplate = this._globalIconTemplate.asReadonly();\r\n\r\n public setGlobalIconTemplate(template: IconTemplateType) {\r\n this._globalIconTemplate.set(template);\r\n }\r\n}\r\n","import { NgTemplateOutlet } from '@angular/common';\r\nimport {\r\n afterRenderEffect,\r\n Component,\r\n computed,\r\n inject,\r\n input,\r\n TemplateRef,\r\n viewChild,\r\n} from '@angular/core';\r\nimport { IconType } from '@ngneers/controls/custom-types';\r\n\r\nimport { GlobalIconTemplate } from './global-icon-template';\r\n\r\n@Component({\r\n selector: 'ngn-icon',\r\n templateUrl: './icon.html',\r\n imports: [NgTemplateOutlet],\r\n host: {\r\n '[attr.ngSkipHydration]': 'true',\r\n },\r\n})\r\nexport class Icon {\r\n private readonly _globalIconTemplate = inject(GlobalIconTemplate).globalIconTemplate;\r\n\r\n public readonly defaultIcon = input<string>();\r\n public readonly icon = input<IconType>();\r\n\r\n private readonly _defaultIconTemplate =\r\n viewChild.required<TemplateRef<{ $implicit: IconType }>>('defaultIconTemplate');\r\n\r\n protected readonly usedIconTemplate = computed(() => {\r\n const globalTemplate = this._globalIconTemplate();\r\n if (globalTemplate) {\r\n return globalTemplate;\r\n }\r\n return this._defaultIconTemplate();\r\n });\r\n\r\n constructor() {\r\n afterRenderEffect(() => {\r\n if (!this.icon() && !this.defaultIcon()) {\r\n throw new Error('Icon component requires either an icon or a default icon to be set.');\r\n }\r\n });\r\n }\r\n}\r\n","@if (icon()) {\r\n <ng-container *ngTemplateOutlet=\"usedIconTemplate(); context: { $implicit: icon() }\">\r\n </ng-container>\r\n\r\n <ng-template #defaultIconTemplate let-icon><i class=\"{{ icon }}\"></i></ng-template>\r\n} @else {\r\n <i class=\"{{ defaultIcon() }}\"></i>\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { input, signal, effect, Component } from '@angular/core';
4
+
5
+ class LazyCacher {
6
+ lazyContent = input(undefined);
7
+ open = input(false);
8
+ cache = input(false);
9
+ hasBeenOpened = signal(false);
10
+ constructor() {
11
+ effect(() => {
12
+ if (this.open()) {
13
+ this.hasBeenOpened.set(true);
14
+ }
15
+ });
16
+ }
17
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: LazyCacher, deps: [], target: i0.ɵɵFactoryTarget.Component });
18
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.5", type: LazyCacher, isStandalone: true, selector: "ngn-lazy-cacher", inputs: { lazyContent: { classPropertyName: "lazyContent", publicName: "lazyContent", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, cache: { classPropertyName: "cache", publicName: "cache", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (lazyContent()) {\r\n @if (open() || (cache() && hasBeenOpened())) {\r\n <ng-container *ngTemplateOutlet=\"lazyContent()\"></ng-container>\r\n }\r\n} @else {\r\n <ng-content></ng-content>\r\n}\r\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
19
+ }
20
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: LazyCacher, decorators: [{
21
+ type: Component,
22
+ args: [{ selector: 'ngn-lazy-cacher', imports: [NgTemplateOutlet], template: "@if (lazyContent()) {\r\n @if (open() || (cache() && hasBeenOpened())) {\r\n <ng-container *ngTemplateOutlet=\"lazyContent()\"></ng-container>\r\n }\r\n} @else {\r\n <ng-content></ng-content>\r\n}\r\n" }]
23
+ }], ctorParameters: () => [] });
24
+
25
+ /**
26
+ * Generated bundle index. Do not edit.
27
+ */
28
+
29
+ export { LazyCacher };
30
+ //# sourceMappingURL=ngneers-controls-lazy-cacher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-lazy-cacher.mjs","sources":["../../../packages/controls/src/lazy-cacher/lazy-cacher.ts","../../../packages/controls/src/lazy-cacher/lazy-cacher.html","../../../packages/controls/src/lazy-cacher/ngneers-controls-lazy-cacher.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\r\nimport { Component, effect, input, signal, TemplateRef } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'ngn-lazy-cacher',\r\n templateUrl: './lazy-cacher.html',\r\n imports: [NgTemplateOutlet],\r\n})\r\nexport class LazyCacher {\r\n public readonly lazyContent = input<TemplateRef<unknown> | undefined | null>(undefined);\r\n public readonly open = input<boolean>(false);\r\n public readonly cache = input<boolean>(false);\r\n\r\n protected readonly hasBeenOpened = signal(false);\r\n\r\n constructor() {\r\n effect(() => {\r\n if (this.open()) {\r\n this.hasBeenOpened.set(true);\r\n }\r\n });\r\n }\r\n}\r\n","@if (lazyContent()) {\r\n @if (open() || (cache() && hasBeenOpened())) {\r\n <ng-container *ngTemplateOutlet=\"lazyContent()\"></ng-container>\r\n }\r\n} @else {\r\n <ng-content></ng-content>\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAQa,UAAU,CAAA;AACL,IAAA,WAAW,GAAG,KAAK,CAA0C,SAAS,CAAC;AACvE,IAAA,IAAI,GAAG,KAAK,CAAU,KAAK,CAAC;AAC5B,IAAA,KAAK,GAAG,KAAK,CAAU,KAAK,CAAC;AAE1B,IAAA,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC;AAEhD,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE;AACf,gBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;;AAEhC,SAAC,CAAC;;uGAZO,UAAU,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAV,UAAU,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECRvB,gNAOA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDDY,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAEf,UAAU,EAAA,UAAA,EAAA,CAAA;kBALtB,SAAS;+BACE,iBAAiB,EAAA,OAAA,EAElB,CAAC,gBAAgB,CAAC,EAAA,QAAA,EAAA,gNAAA,EAAA;;;AEN7B;;AAEG;;;;"}
@@ -0,0 +1,119 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { viewChild, contentChild, input, computed, Component, signal, effect } from '@angular/core';
4
+ import { ValueControlBase, templateTypesFn, transformToNgnItems, flatItems, mapToItems, NgnTemplate, valueControlBaseProvider } from '@ngneers/controls/api';
5
+ import { Scroller } from '@ngneers/controls/scroller';
6
+
7
+ class ListBoxTemplates extends ValueControlBase {
8
+ // Item template
9
+ _defaultItemTemplate = viewChild.required('defaultItemTemplate');
10
+ _userItemTemplate = contentChild('item');
11
+ templateItem = input(null);
12
+ itemTemplate = computed(() => this._userItemTemplate() ?? this.templateItem() ?? this._defaultItemTemplate());
13
+ // Group template
14
+ _defaultGroupTemplate = viewChild.required('defaultGroupTemplate');
15
+ _userGroupTemplate = contentChild('group');
16
+ templateGroup = input(null);
17
+ groupTemplate = computed(() => this._userGroupTemplate() ?? this.templateGroup() ?? this._defaultGroupTemplate());
18
+ /**
19
+ * Types for the dialog templates.
20
+ */
21
+ templateTypes = templateTypesFn();
22
+ templateTypesInternal = templateTypesFn();
23
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: ListBoxTemplates, deps: null, target: i0.ɵɵFactoryTarget.Component });
24
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.5", type: ListBoxTemplates, isStandalone: true, selector: "ng-component", inputs: { templateItem: { classPropertyName: "templateItem", publicName: "templateItem", isSignal: true, isRequired: false, transformFunction: null }, templateGroup: { classPropertyName: "templateGroup", publicName: "templateGroup", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "_userItemTemplate", first: true, predicate: ["item"], descendants: true, isSignal: true }, { propertyName: "_userGroupTemplate", first: true, predicate: ["group"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "_defaultItemTemplate", first: true, predicate: ["defaultItemTemplate"], descendants: true, isSignal: true }, { propertyName: "_defaultGroupTemplate", first: true, predicate: ["defaultGroupTemplate"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: '', isInline: true });
25
+ }
26
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: ListBoxTemplates, decorators: [{
27
+ type: Component,
28
+ args: [{
29
+ imports: [],
30
+ template: '',
31
+ }]
32
+ }] });
33
+
34
+ class ListBox extends ListBoxTemplates {
35
+ items = input([]);
36
+ fields = input();
37
+ selectable = input(false);
38
+ highlightable = input(true);
39
+ virtual = input();
40
+ itemHeight = input();
41
+ _scroller = viewChild.required(Scroller);
42
+ formattedItems = computed(() => {
43
+ const fields = this.fields();
44
+ const items = this.items();
45
+ if (!fields) {
46
+ return items;
47
+ }
48
+ return transformToNgnItems(items, fields);
49
+ });
50
+ flatItems = computed(() => flatItems(this.formattedItems()));
51
+ currentHighlightedValue = signal(null);
52
+ constructor() {
53
+ super();
54
+ effect(() => {
55
+ const currentHighlightedValue = this.currentHighlightedValue();
56
+ if (!currentHighlightedValue) {
57
+ return;
58
+ }
59
+ const index = flatItems(this.formattedItems()).findIndex(option => option.value === currentHighlightedValue);
60
+ this._scroller().scrollToIndex(index);
61
+ });
62
+ }
63
+ onKeyDown(event) {
64
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
65
+ event.stopPropagation();
66
+ event.preventDefault();
67
+ const flattenedItems = mapToItems(this.formattedItems());
68
+ this.currentHighlightedValue.update(currentValue => {
69
+ const currentHighlightIndex = flattenedItems.findIndex(option => option.value === currentValue);
70
+ if (flattenedItems.length === 0) {
71
+ return null;
72
+ }
73
+ const currentIndex = currentHighlightIndex >= 0
74
+ ? currentHighlightIndex
75
+ : flattenedItems.findIndex(option => option.value === this.value());
76
+ if (currentIndex === -1) {
77
+ return event.key === 'ArrowDown'
78
+ ? flattenedItems[0].value
79
+ : flattenedItems[flattenedItems.length - 1].value;
80
+ }
81
+ const nextIndex = event.key === 'ArrowDown' ? currentIndex + 1 : currentIndex - 1;
82
+ if (nextIndex < 0) {
83
+ return flattenedItems[flattenedItems.length - 1].value;
84
+ }
85
+ if (nextIndex >= flattenedItems.length) {
86
+ return flattenedItems[0].value;
87
+ }
88
+ return flattenedItems[nextIndex].value;
89
+ });
90
+ }
91
+ else if (this.selectable() && (event.key === 'Enter' || event.key === ' ')) {
92
+ const currentHighlightedValue = this.currentHighlightedValue();
93
+ if (currentHighlightedValue !== null) {
94
+ event.stopImmediatePropagation();
95
+ event.preventDefault();
96
+ this.onSelect(currentHighlightedValue);
97
+ }
98
+ }
99
+ }
100
+ onSelect(value) {
101
+ this.value.set(value);
102
+ this.onChange(value);
103
+ this.onTouched();
104
+ this.currentHighlightedValue.set(null);
105
+ }
106
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: ListBox, deps: [], target: i0.ɵɵFactoryTarget.Component });
107
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.5", type: ListBox, isStandalone: true, selector: "ngn-list-box", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, highlightable: { classPropertyName: "highlightable", publicName: "highlightable", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null } }, providers: [valueControlBaseProvider(ListBox)], viewQueries: [{ propertyName: "_scroller", first: true, predicate: Scroller, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\r\n<ul\r\n class=\"ngn-list-box\"\r\n style=\"height: 100%; width: 100%\"\r\n [attr.tabIndex]=\"highlightable() ? 0 : null\"\r\n (keydown)=\"onKeyDown($event)\"\r\n>\r\n <ngn-scroller\r\n [virtual]=\"virtual()\"\r\n [itemHeight]=\"itemHeight() ?? 0\"\r\n [items]=\"flatItems()\"\r\n [templateItem]=\"usedItemTemplate\"\r\n [fieldSticky]=\"'items'\"\r\n #scroller\r\n />\r\n</ul>\r\n\r\n<ng-template #usedItemTemplate [ngnTemplate]=\"scroller.templateTypes.item\" let-itemOrGroup>\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <li\r\n [class.highlighted]=\"itemOrGroup.value === currentHighlightedValue()\"\r\n [class.selected]=\"itemOrGroup.value === value()\"\r\n (click)=\"!itemOrGroup.items && selectable() && onSelect(itemOrGroup.value)\"\r\n [style.position]=\"itemOrGroup.items ? 'sticky' : 'initial'\"\r\n [style.top]=\"itemOrGroup.items ? '0' : 'unset'\"\r\n [style.backgroundColor]=\"itemOrGroup.items ? 'white' : 'unset'\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n itemOrGroup.items ? groupTemplate() : itemTemplate();\r\n context: { $implicit: itemOrGroup }\r\n \"\r\n ></ng-container>\r\n </li>\r\n</ng-template>\r\n\r\n<ng-template #defaultItemTemplate [ngnTemplate]=\"templateTypes.item\" let-item>\r\n <span [attr.data-testid]=\"item?.testId\">{{ item?.label }}</span>\r\n</ng-template>\r\n\r\n<ng-template #defaultGroupTemplate [ngnTemplate]=\"templateTypes.item\" let-group>\r\n <span [attr.data-testid]=\"group?.testId\">{{ group?.label }}</span>\r\n</ng-template>\r\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Scroller, selector: "ngn-scroller", inputs: ["items", "virtual", "itemHeight", "padding", "focusable", "fieldSticky", "templateItem"] }, { kind: "directive", type: NgnTemplate, selector: "ng-template[ngnTemplate]", inputs: ["ngnTemplate"] }] });
108
+ }
109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: ListBox, decorators: [{
110
+ type: Component,
111
+ args: [{ selector: 'ngn-list-box', imports: [NgTemplateOutlet, Scroller, NgnTemplate], providers: [valueControlBaseProvider(ListBox)], template: "<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\r\n<ul\r\n class=\"ngn-list-box\"\r\n style=\"height: 100%; width: 100%\"\r\n [attr.tabIndex]=\"highlightable() ? 0 : null\"\r\n (keydown)=\"onKeyDown($event)\"\r\n>\r\n <ngn-scroller\r\n [virtual]=\"virtual()\"\r\n [itemHeight]=\"itemHeight() ?? 0\"\r\n [items]=\"flatItems()\"\r\n [templateItem]=\"usedItemTemplate\"\r\n [fieldSticky]=\"'items'\"\r\n #scroller\r\n />\r\n</ul>\r\n\r\n<ng-template #usedItemTemplate [ngnTemplate]=\"scroller.templateTypes.item\" let-itemOrGroup>\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <li\r\n [class.highlighted]=\"itemOrGroup.value === currentHighlightedValue()\"\r\n [class.selected]=\"itemOrGroup.value === value()\"\r\n (click)=\"!itemOrGroup.items && selectable() && onSelect(itemOrGroup.value)\"\r\n [style.position]=\"itemOrGroup.items ? 'sticky' : 'initial'\"\r\n [style.top]=\"itemOrGroup.items ? '0' : 'unset'\"\r\n [style.backgroundColor]=\"itemOrGroup.items ? 'white' : 'unset'\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n itemOrGroup.items ? groupTemplate() : itemTemplate();\r\n context: { $implicit: itemOrGroup }\r\n \"\r\n ></ng-container>\r\n </li>\r\n</ng-template>\r\n\r\n<ng-template #defaultItemTemplate [ngnTemplate]=\"templateTypes.item\" let-item>\r\n <span [attr.data-testid]=\"item?.testId\">{{ item?.label }}</span>\r\n</ng-template>\r\n\r\n<ng-template #defaultGroupTemplate [ngnTemplate]=\"templateTypes.item\" let-group>\r\n <span [attr.data-testid]=\"group?.testId\">{{ group?.label }}</span>\r\n</ng-template>\r\n" }]
112
+ }], ctorParameters: () => [] });
113
+
114
+ /**
115
+ * Generated bundle index. Do not edit.
116
+ */
117
+
118
+ export { ListBox };
119
+ //# sourceMappingURL=ngneers-controls-list-box.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-list-box.mjs","sources":["../../../packages/controls/src/list-box/list-box-templates.ts","../../../packages/controls/src/list-box/list-box.ts","../../../packages/controls/src/list-box/list-box.html","../../../packages/controls/src/list-box/ngneers-controls-list-box.ts"],"sourcesContent":["import { Component, computed, contentChild, input, TemplateRef, viewChild } from '@angular/core';\r\nimport { NgnItem, templateTypesFn, ValueControlBase } from '@ngneers/controls/api';\r\n\r\n@Component({\r\n imports: [],\r\n template: '',\r\n})\r\nexport abstract class ListBoxTemplates<T, K extends keyof T> extends ValueControlBase<T[K]> {\r\n // Item template\r\n private readonly _defaultItemTemplate =\r\n viewChild.required<TemplateRef<typeof this.templateTypes.item>>('defaultItemTemplate');\r\n private readonly _userItemTemplate =\r\n contentChild<TemplateRef<typeof this.templateTypes.item>>('item');\r\n public readonly templateItem = input<TemplateRef<typeof this.templateTypes.item> | null>(null);\r\n protected readonly itemTemplate = computed(\r\n () => this._userItemTemplate() ?? this.templateItem() ?? this._defaultItemTemplate()\r\n );\r\n // Group template\r\n private readonly _defaultGroupTemplate =\r\n viewChild.required<TemplateRef<typeof this.templateTypes.item>>('defaultGroupTemplate');\r\n private readonly _userGroupTemplate =\r\n contentChild<TemplateRef<typeof this.templateTypes.item>>('group');\r\n public readonly templateGroup = input<TemplateRef<typeof this.templateTypes.item> | null>(null);\r\n protected readonly groupTemplate = computed(\r\n () => this._userGroupTemplate() ?? this.templateGroup() ?? this._defaultGroupTemplate()\r\n );\r\n\r\n /**\r\n * Types for the dialog templates.\r\n */\r\n public readonly templateTypes = templateTypesFn<{\r\n item: {\r\n $implicit: NgnItem<T, K> | undefined;\r\n };\r\n }>();\r\n\r\n protected readonly templateTypesInternal = templateTypesFn<{\r\n item: {\r\n $implicit: NgnItem<T, K> | undefined;\r\n index: number;\r\n };\r\n }>();\r\n}\r\n","import { NgTemplateOutlet } from '@angular/common';\r\nimport { Component, computed, effect, input, signal, viewChild } from '@angular/core';\r\nimport {\r\n flatItems,\r\n mapToItems,\r\n NgnItem,\r\n NgnItemFields,\r\n NgnTemplate,\r\n transformToNgnItems,\r\n valueControlBaseProvider,\r\n} from '@ngneers/controls/api';\r\nimport { Scroller } from '@ngneers/controls/scroller';\r\n\r\nimport { ListBoxTemplates } from './list-box-templates';\r\n\r\n@Component({\r\n selector: 'ngn-list-box',\r\n templateUrl: './list-box.html',\r\n imports: [NgTemplateOutlet, Scroller, NgnTemplate],\r\n providers: [valueControlBaseProvider(ListBox)],\r\n})\r\nexport class ListBox<T extends object, K extends keyof T> extends ListBoxTemplates<T, K> {\r\n public readonly items = input<readonly NgnItem<T, K>[] | readonly T[]>([]);\r\n public readonly fields = input<NgnItemFields<T, K>>();\r\n\r\n public readonly selectable = input<boolean>(false);\r\n public readonly highlightable = input<boolean>(true);\r\n public readonly virtual = input<boolean>();\r\n public readonly itemHeight = input<number>();\r\n private readonly _scroller = viewChild.required<Scroller<T>>(Scroller);\r\n\r\n protected readonly formattedItems = computed(() => {\r\n const fields = this.fields();\r\n const items = this.items();\r\n if (!fields) {\r\n return items as NgnItem<T, K>[];\r\n }\r\n return transformToNgnItems(items as readonly T[], fields);\r\n });\r\n\r\n protected readonly flatItems = computed(() => flatItems(this.formattedItems()));\r\n\r\n protected readonly currentHighlightedValue = signal<T[K] | null>(null);\r\n\r\n constructor() {\r\n super();\r\n\r\n effect(() => {\r\n const currentHighlightedValue = this.currentHighlightedValue();\r\n if (!currentHighlightedValue) {\r\n return;\r\n }\r\n const index = flatItems(this.formattedItems()).findIndex(\r\n option => option.value === currentHighlightedValue\r\n );\r\n this._scroller().scrollToIndex(index);\r\n });\r\n }\r\n\r\n public onKeyDown(event: KeyboardEvent) {\r\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\r\n event.stopPropagation();\r\n event.preventDefault();\r\n const flattenedItems = mapToItems(this.formattedItems());\r\n this.currentHighlightedValue.update(currentValue => {\r\n const currentHighlightIndex = flattenedItems.findIndex(\r\n option => option.value === currentValue\r\n );\r\n if (flattenedItems.length === 0) {\r\n return null;\r\n }\r\n const currentIndex =\r\n currentHighlightIndex >= 0\r\n ? currentHighlightIndex\r\n : flattenedItems.findIndex(option => option.value === this.value());\r\n\r\n if (currentIndex === -1) {\r\n return event.key === 'ArrowDown'\r\n ? flattenedItems[0].value\r\n : flattenedItems[flattenedItems.length - 1].value;\r\n }\r\n\r\n const nextIndex = event.key === 'ArrowDown' ? currentIndex + 1 : currentIndex - 1;\r\n if (nextIndex < 0) {\r\n return flattenedItems[flattenedItems.length - 1].value;\r\n }\r\n if (nextIndex >= flattenedItems.length) {\r\n return flattenedItems[0].value;\r\n }\r\n return flattenedItems[nextIndex].value;\r\n });\r\n } else if (this.selectable() && (event.key === 'Enter' || event.key === ' ')) {\r\n const currentHighlightedValue = this.currentHighlightedValue();\r\n if (currentHighlightedValue !== null) {\r\n event.stopImmediatePropagation();\r\n event.preventDefault();\r\n this.onSelect(currentHighlightedValue);\r\n }\r\n }\r\n }\r\n\r\n protected onSelect(value: T[K]) {\r\n this.value.set(value);\r\n this.onChange(value);\r\n this.onTouched();\r\n this.currentHighlightedValue.set(null);\r\n }\r\n}\r\n","<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\r\n<ul\r\n class=\"ngn-list-box\"\r\n style=\"height: 100%; width: 100%\"\r\n [attr.tabIndex]=\"highlightable() ? 0 : null\"\r\n (keydown)=\"onKeyDown($event)\"\r\n>\r\n <ngn-scroller\r\n [virtual]=\"virtual()\"\r\n [itemHeight]=\"itemHeight() ?? 0\"\r\n [items]=\"flatItems()\"\r\n [templateItem]=\"usedItemTemplate\"\r\n [fieldSticky]=\"'items'\"\r\n #scroller\r\n />\r\n</ul>\r\n\r\n<ng-template #usedItemTemplate [ngnTemplate]=\"scroller.templateTypes.item\" let-itemOrGroup>\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <li\r\n [class.highlighted]=\"itemOrGroup.value === currentHighlightedValue()\"\r\n [class.selected]=\"itemOrGroup.value === value()\"\r\n (click)=\"!itemOrGroup.items && selectable() && onSelect(itemOrGroup.value)\"\r\n [style.position]=\"itemOrGroup.items ? 'sticky' : 'initial'\"\r\n [style.top]=\"itemOrGroup.items ? '0' : 'unset'\"\r\n [style.backgroundColor]=\"itemOrGroup.items ? 'white' : 'unset'\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n itemOrGroup.items ? groupTemplate() : itemTemplate();\r\n context: { $implicit: itemOrGroup }\r\n \"\r\n ></ng-container>\r\n </li>\r\n</ng-template>\r\n\r\n<ng-template #defaultItemTemplate [ngnTemplate]=\"templateTypes.item\" let-item>\r\n <span [attr.data-testid]=\"item?.testId\">{{ item?.label }}</span>\r\n</ng-template>\r\n\r\n<ng-template #defaultGroupTemplate [ngnTemplate]=\"templateTypes.item\" let-group>\r\n <span [attr.data-testid]=\"group?.testId\">{{ group?.label }}</span>\r\n</ng-template>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, contentChild, computed, output, signal, inject, Injector, viewChild, Component } from '@angular/core';
3
+ import { autoPositionElement } from '@ngneers/controls/api';
4
+ import { LazyCacher } from '@ngneers/controls/lazy-cacher';
5
+ import { computedWithPrevious } from '@ngneers/controls/utils';
6
+
7
+ class Popover {
8
+ anchor = input.required();
9
+ options = input();
10
+ lazyContent = contentChild('lazy');
11
+ appliedOptions = computed(() => ({
12
+ cache: false,
13
+ ...this.options(),
14
+ }));
15
+ opened = output();
16
+ closed = output();
17
+ _isOpen = signal(false);
18
+ isOpen = this._isOpen.asReadonly();
19
+ _injector = inject(Injector);
20
+ _popoverRef = viewChild('popover');
21
+ _popover = computed(() => this._popoverRef()?.nativeElement);
22
+ _autoPos = computedWithPrevious(prev => {
23
+ prev?.stop();
24
+ const popoverEl = this._popover();
25
+ if (!popoverEl) {
26
+ return undefined;
27
+ }
28
+ return autoPositionElement(this.anchor(), popoverEl, {
29
+ injector: this._injector,
30
+ stopped: true,
31
+ sizeConstraints: this.appliedOptions()?.sizeConstraints,
32
+ });
33
+ });
34
+ open() {
35
+ if (this.isOpen()) {
36
+ return;
37
+ }
38
+ this._autoPos()?.start();
39
+ this._popover()?.togglePopover();
40
+ }
41
+ toggle() {
42
+ if (this.isOpen()) {
43
+ this.close();
44
+ }
45
+ else {
46
+ this.open();
47
+ }
48
+ }
49
+ close() {
50
+ if (!this.isOpen()) {
51
+ return;
52
+ }
53
+ this._popover()?.togglePopover();
54
+ }
55
+ onToggle(event) {
56
+ const evt = event;
57
+ if (evt.newState === 'closed') {
58
+ this._isOpen.set(false);
59
+ this.closed.emit();
60
+ this._autoPos()?.stop();
61
+ }
62
+ else {
63
+ this._isOpen.set(true);
64
+ this.opened.emit();
65
+ }
66
+ }
67
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: Popover, deps: [], target: i0.ɵɵFactoryTarget.Component });
68
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.5", type: Popover, isStandalone: true, selector: "ngn-popover", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened", closed: "closed" }, queries: [{ propertyName: "lazyContent", first: true, predicate: ["lazy"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "_popoverRef", first: true, predicate: ["popover"], descendants: true, isSignal: true }], ngImport: i0, template: "<div popover (toggle)=\"onToggle($event)\" #popover id=\"dropdown\">\r\n <ngn-lazy-cacher [lazyContent]=\"lazyContent()\" [open]=\"isOpen()\" [cache]=\"appliedOptions().cache\">\r\n <ng-content></ng-content>\r\n </ngn-lazy-cacher>\r\n</div>\r\n", dependencies: [{ kind: "component", type: LazyCacher, selector: "ngn-lazy-cacher", inputs: ["lazyContent", "open", "cache"] }] });
69
+ }
70
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: Popover, decorators: [{
71
+ type: Component,
72
+ args: [{ selector: 'ngn-popover', imports: [LazyCacher], template: "<div popover (toggle)=\"onToggle($event)\" #popover id=\"dropdown\">\r\n <ngn-lazy-cacher [lazyContent]=\"lazyContent()\" [open]=\"isOpen()\" [cache]=\"appliedOptions().cache\">\r\n <ng-content></ng-content>\r\n </ngn-lazy-cacher>\r\n</div>\r\n" }]
73
+ }] });
74
+
75
+ /**
76
+ * Generated bundle index. Do not edit.
77
+ */
78
+
79
+ export { Popover };
80
+ //# sourceMappingURL=ngneers-controls-popover.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-popover.mjs","sources":["../../../packages/controls/src/popover/popover.ts","../../../packages/controls/src/popover/popover.html","../../../packages/controls/src/popover/ngneers-controls-popover.ts"],"sourcesContent":["import {\r\n Component,\r\n computed,\r\n contentChild,\r\n ElementRef,\r\n inject,\r\n Injector,\r\n input,\r\n output,\r\n signal,\r\n TemplateRef,\r\n viewChild,\r\n} from '@angular/core';\r\nimport {\r\n autoPositionElement,\r\n AutoPositioningHandle,\r\n PositioningSizeConstraints,\r\n} from '@ngneers/controls/api';\r\nimport { LazyCacher } from '@ngneers/controls/lazy-cacher';\r\nimport { computedWithPrevious } from '@ngneers/controls/utils';\r\n\r\nexport type PopoverOptions = {\r\n /**\r\n * Constraints for the size of the popover.\r\n */\r\n sizeConstraints?: PositioningSizeConstraints;\r\n /**\r\n * If true, the content of the popover will be cached and not recreated on each open.\r\n * This is useful for performance when the content is expensive to create.\r\n */\r\n cache?: boolean;\r\n};\r\n@Component({\r\n selector: 'ngn-popover',\r\n templateUrl: './popover.html',\r\n imports: [LazyCacher],\r\n})\r\nexport class Popover {\r\n public readonly anchor = input.required<HTMLElement>();\r\n public readonly options = input<PopoverOptions>();\r\n\r\n protected readonly lazyContent = contentChild<TemplateRef<unknown>>('lazy');\r\n\r\n protected readonly appliedOptions = computed(() => ({\r\n cache: false,\r\n ...this.options(),\r\n }));\r\n\r\n public readonly opened = output();\r\n public readonly closed = output();\r\n\r\n private readonly _isOpen = signal(false);\r\n public readonly isOpen = this._isOpen.asReadonly();\r\n\r\n private readonly _injector = inject(Injector);\r\n private readonly _popoverRef = viewChild<ElementRef<HTMLElement>>('popover');\r\n private readonly _popover = computed(() => this._popoverRef()?.nativeElement);\r\n private readonly _autoPos = computedWithPrevious<AutoPositioningHandle | undefined>(prev => {\r\n prev?.stop();\r\n const popoverEl = this._popover();\r\n if (!popoverEl) {\r\n return undefined;\r\n }\r\n return autoPositionElement(this.anchor(), popoverEl, {\r\n injector: this._injector,\r\n stopped: true,\r\n sizeConstraints: this.appliedOptions()?.sizeConstraints,\r\n });\r\n });\r\n\r\n public open() {\r\n if (this.isOpen()) {\r\n return;\r\n }\r\n this._autoPos()?.start();\r\n this._popover()?.togglePopover();\r\n }\r\n\r\n public toggle() {\r\n if (this.isOpen()) {\r\n this.close();\r\n } else {\r\n this.open();\r\n }\r\n }\r\n\r\n public close() {\r\n if (!this.isOpen()) {\r\n return;\r\n }\r\n this._popover()?.togglePopover();\r\n }\r\n\r\n protected onToggle(event: Event) {\r\n const evt = event as ToggleEvent;\r\n if (evt.newState === 'closed') {\r\n this._isOpen.set(false);\r\n this.closed.emit();\r\n this._autoPos()?.stop();\r\n } else {\r\n this._isOpen.set(true);\r\n this.opened.emit();\r\n }\r\n }\r\n}\r\n","<div popover (toggle)=\"onToggle($event)\" #popover id=\"dropdown\">\r\n <ngn-lazy-cacher [lazyContent]=\"lazyContent()\" [open]=\"isOpen()\" [cache]=\"appliedOptions().cache\">\r\n <ng-content></ng-content>\r\n </ngn-lazy-cacher>\r\n</div>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { input, viewChild, contentChild, computed, inject, ElementRef, effect, untracked, Component } from '@angular/core';
4
+ import { toSignal } from '@angular/core/rxjs-interop';
5
+ import { elementSizeSignal, templateTypesFn } from '@ngneers/controls/api';
6
+ import { fromEvent, map } from 'rxjs';
7
+
8
+ class Scroller {
9
+ /**
10
+ * The items to be displayed in the scroller.
11
+ * This is a required input and should be an array of items of type T.
12
+ */
13
+ items = input.required();
14
+ /**
15
+ * Whether the scroller should use virtual scrolling.
16
+ * When set to true, the scroller will only render the items that are currently visible
17
+ * on the screen, improving performance for large lists.
18
+ *
19
+ * When virtual scrolling is enabled, {@link itemHeight} must be set to a non-zero value.
20
+ * @defaultValue `false`
21
+ */
22
+ virtual = input(false);
23
+ /**
24
+ * When {@link virtual} scrolling is enabled, this input is required to define the height of each item in pixels.
25
+ */
26
+ itemHeight = input(0);
27
+ /**
28
+ * When {@link virtual} scrolling is enabled, this input defines the number of items to be rendered above and below the visible area.
29
+ * This can help to reduce flickering when scrolling.
30
+ * @defaultValue `2`
31
+ */
32
+ padding = input(2);
33
+ /**
34
+ * Determines whether the scroller should have a tab index and be focusable.
35
+ * @defaultValue `false`
36
+ */
37
+ focusable = input(false);
38
+ /**
39
+ * Determines whether an item is sticky or not.
40
+ * If set, the scroller will stick the items with a truthy value for the specified field to the top of the scroller.
41
+ */
42
+ fieldSticky = input(null);
43
+ _itemList = viewChild.required('itemList');
44
+ /**
45
+ * The template to be used for rendering each item in the scroller.
46
+ * Can also be set using the `item` content child.
47
+ */
48
+ templateItem = input(null);
49
+ _userItemTemplate = contentChild('item');
50
+ itemTemplate = computed(() => this._userItemTemplate() ?? this.templateItem());
51
+ _el = inject((ElementRef));
52
+ _scrollElement = this._el.nativeElement;
53
+ _elementSize = elementSizeSignal(this._scrollElement);
54
+ _visibleItemCount = computed(() => this.virtual()
55
+ ? Math.ceil(this._elementSize().height / this.itemHeight() + this.padding() * 2)
56
+ : 0);
57
+ _scrollTop = toSignal(fromEvent(this._scrollElement, 'scroll').pipe(map(e => e.target.scrollTop)), { initialValue: 0 });
58
+ _itemStartIndex = computed(() => this.virtual()
59
+ ? Math.max(0, Math.ceil(this._scrollTop() / this.itemHeight()) - this.padding())
60
+ : 0);
61
+ _itemEndIndex = computed(() => this.virtual()
62
+ ? Math.min(this.items().length, this._itemStartIndex() + this._visibleItemCount())
63
+ : 0);
64
+ visibleItems = computed(() => {
65
+ if (!this.virtual()) {
66
+ return this.items().map((item, index) => ({ item, index }));
67
+ }
68
+ return this.items()
69
+ .slice(this._itemStartIndex(), this._itemEndIndex())
70
+ .map((item, index) => {
71
+ return {
72
+ item,
73
+ index: this._itemStartIndex() + index,
74
+ };
75
+ });
76
+ });
77
+ /**
78
+ * Returns the last sticky item that is not rendered regularly in the list.
79
+ * Required because sticky items might need to be rendered longer than they would.
80
+ * Only applicable when virtual scrolling is enabled.
81
+ */
82
+ latestStickyItem = computed(() => {
83
+ if (!this.virtual()) {
84
+ return null;
85
+ }
86
+ const stickyField = this.fieldSticky();
87
+ if (!stickyField) {
88
+ return null;
89
+ }
90
+ const unrenderedItemsAtTop = this.items()
91
+ .slice(0, this._itemStartIndex())
92
+ .map((item, index) => ({
93
+ item,
94
+ index,
95
+ }));
96
+ const stickyItems = unrenderedItemsAtTop.filter(item => item.item[stickyField]);
97
+ return stickyItems[stickyItems.length - 1] ?? null;
98
+ });
99
+ itemsTop = computed(() => {
100
+ return (this._itemStartIndex() - (this.latestStickyItem() ? 1 : 0)) * this.itemHeight();
101
+ });
102
+ dummyHeight = computed(() => {
103
+ return this.items().length * this.itemHeight();
104
+ });
105
+ constructor() {
106
+ effect(() => {
107
+ if (this.virtual() && !this.itemHeight()) {
108
+ throw new Error('Item height must be set when virtual is true');
109
+ }
110
+ });
111
+ }
112
+ /**
113
+ * Scrolls the item with the given index into view.
114
+ * @param index The index of the item to scroll to.
115
+ */
116
+ scrollToIndex(index) {
117
+ untracked(() => {
118
+ if (this.virtual()) {
119
+ const scrollTop = this._scrollTop();
120
+ const visibleHeight = this._elementSize().height;
121
+ const itemTop = index * this.itemHeight();
122
+ const itemBottom = itemTop + this.itemHeight();
123
+ if (itemTop < scrollTop) {
124
+ this._scrollElement.scrollTo({
125
+ top: itemTop - 10,
126
+ });
127
+ }
128
+ else if (itemBottom > scrollTop + visibleHeight) {
129
+ this._scrollElement.scrollTo({
130
+ top: itemBottom - visibleHeight + 10,
131
+ });
132
+ }
133
+ }
134
+ else {
135
+ const itemElement = this._itemList().nativeElement.querySelector(`:nth-child(${index + 1})`);
136
+ if (itemElement) {
137
+ itemElement.scrollIntoView({ block: 'nearest' });
138
+ }
139
+ }
140
+ });
141
+ }
142
+ /**
143
+ * Template types for the scroller.
144
+ * Can be used with the {@link @ngneers/controls/api#NgnTemplate | NgnTemplate} directive for type safe ng-templates.
145
+ */
146
+ templateTypes = templateTypesFn();
147
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: Scroller, deps: [], target: i0.ɵɵFactoryTarget.Component });
148
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.5", type: Scroller, isStandalone: true, selector: "ngn-scroller", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, focusable: { classPropertyName: "focusable", publicName: "focusable", isSignal: true, isRequired: false, transformFunction: null }, fieldSticky: { classPropertyName: "fieldSticky", publicName: "fieldSticky", isSignal: true, isRequired: false, transformFunction: null }, templateItem: { classPropertyName: "templateItem", publicName: "templateItem", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "tabIndex": "focusable() ? 0 : -1" }, styleAttribute: "position: relative; overflow: auto; display: block; height: 100%; width: 100%;" }, queries: [{ propertyName: "_userItemTemplate", first: true, predicate: ["item"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "_itemList", first: true, predicate: ["itemList"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (virtual()) {\r\n <div [style.height.px]=\"dummyHeight()\"></div>\r\n}\r\n<div #itemList [style.top.px]=\"itemsTop()\" [style.position]=\"virtual() ? 'absolute' : 'unset'\">\r\n @if (latestStickyItem(); as latestStickyItem) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n itemTemplate();\r\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\r\n \"\r\n ></ng-container>\r\n }\r\n @for (item of visibleItems(); track item.index) {\r\n <ng-container\r\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\r\n ></ng-container>\r\n }\r\n</div>\r\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
149
+ }
150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: Scroller, decorators: [{
151
+ type: Component,
152
+ args: [{ selector: 'ngn-scroller', imports: [NgTemplateOutlet], host: {
153
+ style: 'position: relative; overflow: auto; display: block; height: 100%; width: 100%;',
154
+ '[tabIndex]': 'focusable() ? 0 : -1',
155
+ }, template: "@if (virtual()) {\r\n <div [style.height.px]=\"dummyHeight()\"></div>\r\n}\r\n<div #itemList [style.top.px]=\"itemsTop()\" [style.position]=\"virtual() ? 'absolute' : 'unset'\">\r\n @if (latestStickyItem(); as latestStickyItem) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n itemTemplate();\r\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\r\n \"\r\n ></ng-container>\r\n }\r\n @for (item of visibleItems(); track item.index) {\r\n <ng-container\r\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\r\n ></ng-container>\r\n }\r\n</div>\r\n" }]
156
+ }], ctorParameters: () => [] });
157
+
158
+ /**
159
+ * Generated bundle index. Do not edit.
160
+ */
161
+
162
+ export { Scroller };
163
+ //# sourceMappingURL=ngneers-controls-scroller.mjs.map