@odx/angular 12.19.1 → 12.20.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/CHANGELOG.md +12 -0
- package/cdk/scrollable/README.md +3 -0
- package/cdk/scrollable/index.d.ts +1 -0
- package/cdk/scrollable/lib/scrollable.service.d.ts +53 -0
- package/components/spinbox/lib/spinbox.component.d.ts +10 -8
- package/components/tab-bar/lib/components/tab-bar-item/tab-bar-item.component.d.ts +4 -5
- package/components/tab-bar/lib/tab-bar.component.d.ts +22 -44
- package/esm2022/cdk/scrollable/index.mjs +2 -0
- package/esm2022/cdk/scrollable/lib/scrollable.service.mjs +108 -0
- package/esm2022/cdk/scrollable/odx-angular-cdk-scrollable.mjs +5 -0
- package/esm2022/components/spinbox/lib/spinbox.component.mjs +61 -25
- package/esm2022/components/tab-bar/lib/components/tab-bar-item/tab-bar-item.component.mjs +10 -17
- package/esm2022/components/tab-bar/lib/tab-bar.component.mjs +75 -114
- package/fesm2022/odx-angular-cdk-scrollable.mjs +115 -0
- package/fesm2022/odx-angular-cdk-scrollable.mjs.map +1 -0
- package/fesm2022/odx-angular-components-spinbox.mjs +60 -24
- package/fesm2022/odx-angular-components-spinbox.mjs.map +1 -1
- package/fesm2022/odx-angular-components-tab-bar.mjs +82 -128
- package/fesm2022/odx-angular-components-tab-bar.mjs.map +1 -1
- package/package.json +7 -1
|
@@ -5,9 +5,7 @@ import { detectControllerChanges, DisabledController, ReadonlyController } from
|
|
|
5
5
|
import { CustomFormControl, ControlDirective } from '@odx/angular/cdk/custom-form-control';
|
|
6
6
|
import { IconComponent } from '@odx/angular/components/icon';
|
|
7
7
|
import { CSSComponent } from '@odx/angular/internal';
|
|
8
|
-
import {
|
|
9
|
-
import { untilDestroyed, injectElement } from '@odx/angular/utils';
|
|
10
|
-
import { filter, tap, switchMap, NEVER, timer, startWith } from 'rxjs';
|
|
8
|
+
import { injectElement } from '@odx/angular/utils';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* SpinboxComponent is a custom form control for numeric input which allows users to increment and decrement
|
|
@@ -22,22 +20,21 @@ let SpinboxComponent = class SpinboxComponent extends CustomFormControl {
|
|
|
22
20
|
}
|
|
23
21
|
constructor() {
|
|
24
22
|
super(0);
|
|
25
|
-
this.takeUntilDestroyed = untilDestroyed();
|
|
26
23
|
this.element = injectElement();
|
|
27
24
|
/**
|
|
28
25
|
* The maximum allowable value for the spinbox.
|
|
29
26
|
*
|
|
30
27
|
* @type {number}
|
|
31
|
-
* @default
|
|
28
|
+
* @default Number.POSITIVE_INFINITY
|
|
32
29
|
*/
|
|
33
|
-
this.max =
|
|
30
|
+
this.max = Number.POSITIVE_INFINITY;
|
|
34
31
|
/**
|
|
35
32
|
* The minimum allowable value for the spinbox.
|
|
36
33
|
*
|
|
37
34
|
* @type {number}
|
|
38
|
-
* @default
|
|
35
|
+
* @default Number.NEGATIVE_INFINITY
|
|
39
36
|
*/
|
|
40
|
-
this.min =
|
|
37
|
+
this.min = Number.NEGATIVE_INFINITY;
|
|
41
38
|
/**
|
|
42
39
|
* The value increment or decrement step for the spinbox.
|
|
43
40
|
*
|
|
@@ -48,15 +45,25 @@ let SpinboxComponent = class SpinboxComponent extends CustomFormControl {
|
|
|
48
45
|
detectControllerChanges(this).subscribe();
|
|
49
46
|
}
|
|
50
47
|
ngAfterViewInit() {
|
|
51
|
-
this.
|
|
48
|
+
this.destroyDecrementListener = this.pressAndHold(this.decrementButton.nativeElement, () => {
|
|
52
49
|
this.inputElement?.stepDown();
|
|
53
|
-
this.
|
|
50
|
+
this.updateValue(numberAttribute(this.inputElement.value));
|
|
54
51
|
});
|
|
55
|
-
this.
|
|
52
|
+
this.destroyIncrementListener = this.pressAndHold(this.incrementButton.nativeElement, () => {
|
|
56
53
|
this.inputElement?.stepUp();
|
|
57
|
-
this.
|
|
54
|
+
this.updateValue(numberAttribute(this.inputElement.value));
|
|
58
55
|
});
|
|
59
56
|
}
|
|
57
|
+
ngOnDestroy() {
|
|
58
|
+
super.ngOnDestroy();
|
|
59
|
+
this.destroyDecrementListener?.();
|
|
60
|
+
this.destroyIncrementListener?.();
|
|
61
|
+
}
|
|
62
|
+
updateValue(value) {
|
|
63
|
+
if (this.value === value)
|
|
64
|
+
return;
|
|
65
|
+
super.updateValue(value);
|
|
66
|
+
}
|
|
60
67
|
maxSteps({ code }) {
|
|
61
68
|
if (code === 'End' && this.min !== -Infinity) {
|
|
62
69
|
this.updateValue(this.min);
|
|
@@ -65,21 +72,50 @@ let SpinboxComponent = class SpinboxComponent extends CustomFormControl {
|
|
|
65
72
|
this.updateValue(this.max);
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
|
-
|
|
75
|
+
checkAndUpdate() {
|
|
69
76
|
const value = numberAttribute(this.inputElement.value);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.updateValue(numberAttribute(this.inputElement.value));
|
|
77
|
+
const clampedValue = Math.min(this.max, Math.max(this.min, value));
|
|
78
|
+
this.inputElement.value = clampedValue.toString();
|
|
79
|
+
this.updateValue(clampedValue);
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
pressAndHold(element, handler, interval = 200) {
|
|
82
|
+
let timerId;
|
|
83
|
+
const clearTimer = () => {
|
|
84
|
+
clearInterval(timerId);
|
|
85
|
+
timerId = undefined;
|
|
86
|
+
};
|
|
87
|
+
const start = () => {
|
|
88
|
+
handler();
|
|
89
|
+
timerId = window.setInterval(handler, interval);
|
|
90
|
+
};
|
|
91
|
+
const handleKeyDown = (event) => {
|
|
92
|
+
if ((event.code === 'Space' || event.code === 'Enter') && timerId === undefined) {
|
|
93
|
+
if (event.code === 'Space') {
|
|
94
|
+
event.preventDefault(); // blocks scrolling
|
|
95
|
+
}
|
|
96
|
+
start();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const handleKeyUp = (event) => {
|
|
100
|
+
if (event.code === 'Space' || event.code === 'Enter') {
|
|
101
|
+
clearTimer();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
element.addEventListener('mousedown', start);
|
|
105
|
+
element.addEventListener('mouseup', clearTimer);
|
|
106
|
+
element.addEventListener('mouseout', clearTimer);
|
|
107
|
+
element.addEventListener('keydown', handleKeyDown);
|
|
108
|
+
element.addEventListener('keyup', handleKeyUp);
|
|
109
|
+
return () => {
|
|
110
|
+
element.removeEventListener('mousedown', start);
|
|
111
|
+
element.removeEventListener('mouseup', clearTimer);
|
|
112
|
+
element.removeEventListener('mouseout', clearTimer);
|
|
113
|
+
element.removeEventListener('keydown', handleKeyDown);
|
|
114
|
+
element.removeEventListener('keyup', handleKeyUp);
|
|
115
|
+
};
|
|
80
116
|
}
|
|
81
117
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SpinboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
82
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "18.2.13", type: SpinboxComponent, isStandalone: true, selector: "odx-spinbox", inputs: { max: ["max", "max", numberAttribute], min: ["min", "min", numberAttribute], step: ["step", "step", numberAttribute] }, providers: [DisabledController.connect(), ReadonlyController.connect()], viewQueries: [{ propertyName: "decrementButton", first: true, predicate: ["decrementButton"], descendants: true }, { propertyName: "incrementButton", first: true, predicate: ["incrementButton"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #decrementButton><odx-icon name=\"minus\" /></button>\n<input\n odxControl\n class=\"odx-spinbox__input\"\n [disabled]=\"isDisabled\"\n [readonly]=\"isReadonly\"\n type=\"number\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n (keydown)=\"maxSteps($event)\"\n (change)=\"
|
|
118
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "18.2.13", type: SpinboxComponent, isStandalone: true, selector: "odx-spinbox", inputs: { max: ["max", "max", numberAttribute], min: ["min", "min", numberAttribute], step: ["step", "step", numberAttribute] }, providers: [DisabledController.connect(), ReadonlyController.connect()], viewQueries: [{ propertyName: "decrementButton", first: true, predicate: ["decrementButton"], descendants: true }, { propertyName: "incrementButton", first: true, predicate: ["incrementButton"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #decrementButton><odx-icon name=\"minus\" /></button>\n<input\n odxControl\n class=\"odx-spinbox__input\"\n [disabled]=\"isDisabled\"\n [readonly]=\"isReadonly\"\n type=\"number\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n (keydown)=\"maxSteps($event)\"\n (change)=\"checkAndUpdate()\"\n (blur)=\"checkAndUpdate()\"\n/>\n<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #incrementButton><odx-icon name=\"plus\" /></button>\n", dependencies: [{ kind: "directive", type: ControlDirective, selector: "[odxControl]", exportAs: ["odxControl"] }, { kind: "component", type: IconComponent, selector: "odx-icon", inputs: ["inline", "size", "name", "iconSet", "identifier"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
83
119
|
};
|
|
84
120
|
SpinboxComponent = __decorate([
|
|
85
121
|
CSSComponent('spinbox'),
|
|
@@ -87,7 +123,7 @@ SpinboxComponent = __decorate([
|
|
|
87
123
|
], SpinboxComponent);
|
|
88
124
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SpinboxComponent, decorators: [{
|
|
89
125
|
type: Component,
|
|
90
|
-
args: [{ standalone: true, selector: 'odx-spinbox', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [ControlDirective, IconComponent], providers: [DisabledController.connect(), ReadonlyController.connect()], template: "<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #decrementButton><odx-icon name=\"minus\" /></button>\n<input\n odxControl\n class=\"odx-spinbox__input\"\n [disabled]=\"isDisabled\"\n [readonly]=\"isReadonly\"\n type=\"number\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n (keydown)=\"maxSteps($event)\"\n (change)=\"
|
|
126
|
+
args: [{ standalone: true, selector: 'odx-spinbox', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [ControlDirective, IconComponent], providers: [DisabledController.connect(), ReadonlyController.connect()], template: "<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #decrementButton><odx-icon name=\"minus\" /></button>\n<input\n odxControl\n class=\"odx-spinbox__input\"\n [disabled]=\"isDisabled\"\n [readonly]=\"isReadonly\"\n type=\"number\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n (keydown)=\"maxSteps($event)\"\n (change)=\"checkAndUpdate()\"\n (blur)=\"checkAndUpdate()\"\n/>\n<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #incrementButton><odx-icon name=\"plus\" /></button>\n" }]
|
|
91
127
|
}], ctorParameters: () => [], propDecorators: { decrementButton: [{
|
|
92
128
|
type: ViewChild,
|
|
93
129
|
args: ['decrementButton']
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odx-angular-components-spinbox.mjs","sources":["../../../../libs/angular/components/spinbox/src/lib/spinbox.component.ts","../../../../libs/angular/components/spinbox/src/lib/spinbox.component.html","../../../../libs/angular/components/spinbox/src/odx-angular-components-spinbox.ts"],"sourcesContent":["import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, numberAttribute, ViewChild, ViewEncapsulation } from '@angular/core';\nimport { detectControllerChanges, DisabledController, ReadonlyController } from '@odx/angular';\nimport { ControlDirective, CustomFormControl } from '@odx/angular/cdk/custom-form-control';\nimport { IconComponent } from '@odx/angular/components/icon';\nimport { CSSComponent } from '@odx/angular/internal';\nimport { fromEvents } from '@odx/angular/rxjs';\nimport { injectElement, untilDestroyed } from '@odx/angular/utils';\nimport { filter, NEVER, Observable, startWith, switchMap, tap, timer } from 'rxjs';\n\n/**\n * SpinboxComponent is a custom form control for numeric input which allows users to increment and decrement\n * the value using corresponding buttons. It supports keyboard navigation and automatic boundary checks for the values.\n * Extends CustomFormControl to provide additional behavior.\n *\n * @see {CustomFormControl}\n */\n@CSSComponent('spinbox')\n@Component({\n standalone: true,\n selector: 'odx-spinbox',\n templateUrl: 'spinbox.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [ControlDirective, IconComponent],\n providers: [DisabledController.connect(), ReadonlyController.connect()],\n})\nexport class SpinboxComponent extends CustomFormControl<number> implements AfterViewInit {\n private readonly takeUntilDestroyed = untilDestroyed();\n\n private get inputElement(): HTMLInputElement {\n return this.control?.element.nativeElement as HTMLInputElement;\n }\n\n @ViewChild('decrementButton')\n protected decrementButton!: ElementRef<HTMLButtonElement>;\n\n @ViewChild('incrementButton')\n protected incrementButton!: ElementRef<HTMLButtonElement>;\n\n public readonly element = injectElement();\n\n /**\n * The maximum allowable value for the spinbox.\n *\n * @type {number}\n * @default Infinity\n */\n @Input({ transform: numberAttribute })\n public max = Infinity;\n\n /**\n * The minimum allowable value for the spinbox.\n *\n * @type {number}\n * @default -Infinity\n */\n @Input({ transform: numberAttribute })\n public min = -Infinity;\n\n /**\n * The value increment or decrement step for the spinbox.\n *\n * @type {number}\n * @default 1\n */\n @Input({ transform: numberAttribute })\n public step = 1;\n\n constructor() {\n super(0);\n detectControllerChanges(this).subscribe();\n }\n\n public ngAfterViewInit(): void {\n this.registerPressHold(this.decrementButton).subscribe(() => {\n this.inputElement?.stepDown();\n this.onInputChange();\n });\n this.registerPressHold(this.incrementButton).subscribe(() => {\n this.inputElement?.stepUp();\n this.onInputChange();\n });\n }\n\n protected maxSteps({ code }: KeyboardEvent) {\n if (code === 'End' && this.min !== -Infinity) {\n this.updateValue(this.min);\n } else if (code === 'Home' && this.max !== Infinity) {\n this.updateValue(this.max);\n }\n }\n\n protected checkValue(): void {\n const value = numberAttribute(this.inputElement.value);\n this.updateValue(value < this.min ? this.min : value > this.max ? this.max : value);\n }\n\n protected onInputChange(): void {\n this.updateValue(numberAttribute(this.inputElement.value));\n }\n\n private registerPressHold(target: ElementRef<HTMLElement>): Observable<unknown> {\n return fromEvents(target.nativeElement, 'keydown', 'mousedown', 'mouseup', 'keyup').pipe(\n filter((event) => event?.type !== 'keydown' || ['Space', 'Enter'].includes((event as KeyboardEvent).code)),\n tap((event) => {\n event.preventDefault();\n event.stopPropagation();\n }),\n switchMap(({ type }) => (['mouseup', 'keyup'].includes(type) ? NEVER : timer(500, 100).pipe(startWith(undefined)))),\n this.takeUntilDestroyed(),\n );\n }\n}\n","<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #decrementButton><odx-icon name=\"minus\" /></button>\n<input\n odxControl\n class=\"odx-spinbox__input\"\n [disabled]=\"isDisabled\"\n [readonly]=\"isReadonly\"\n type=\"number\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n (keydown)=\"maxSteps($event)\"\n (change)=\"onInputChange()\"\n (blur)=\"checkValue()\"\n/>\n<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #incrementButton><odx-icon name=\"plus\" /></button>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"odx-angular-components-spinbox.mjs","sources":["../../../../libs/angular/components/spinbox/src/lib/spinbox.component.ts","../../../../libs/angular/components/spinbox/src/lib/spinbox.component.html","../../../../libs/angular/components/spinbox/src/odx-angular-components-spinbox.ts"],"sourcesContent":["import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Input, numberAttribute, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';\nimport { detectControllerChanges, DisabledController, ReadonlyController } from '@odx/angular';\nimport { ControlDirective, CustomFormControl } from '@odx/angular/cdk/custom-form-control';\nimport { IconComponent } from '@odx/angular/components/icon';\nimport { CSSComponent } from '@odx/angular/internal';\nimport { injectElement } from '@odx/angular/utils';\n\n/**\n * SpinboxComponent is a custom form control for numeric input which allows users to increment and decrement\n * the value using corresponding buttons. It supports keyboard navigation and automatic boundary checks for the values.\n * Extends CustomFormControl to provide additional behavior.\n *\n * @see {CustomFormControl}\n */\n@CSSComponent('spinbox')\n@Component({\n standalone: true,\n selector: 'odx-spinbox',\n templateUrl: 'spinbox.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [ControlDirective, IconComponent],\n providers: [DisabledController.connect(), ReadonlyController.connect()],\n})\nexport class SpinboxComponent extends CustomFormControl<number> implements AfterViewInit, OnDestroy {\n private destroyIncrementListener!: () => void;\n private destroyDecrementListener!: () => void;\n private get inputElement(): HTMLInputElement {\n return this.control?.element.nativeElement as HTMLInputElement;\n }\n\n @ViewChild('decrementButton')\n protected decrementButton!: ElementRef<HTMLButtonElement>;\n\n @ViewChild('incrementButton')\n protected incrementButton!: ElementRef<HTMLButtonElement>;\n\n public readonly element = injectElement();\n\n /**\n * The maximum allowable value for the spinbox.\n *\n * @type {number}\n * @default Number.POSITIVE_INFINITY\n */\n @Input({ transform: numberAttribute })\n public max = Number.POSITIVE_INFINITY;\n\n /**\n * The minimum allowable value for the spinbox.\n *\n * @type {number}\n * @default Number.NEGATIVE_INFINITY\n */\n @Input({ transform: numberAttribute })\n public min = Number.NEGATIVE_INFINITY;\n\n /**\n * The value increment or decrement step for the spinbox.\n *\n * @type {number}\n * @default 1\n */\n @Input({ transform: numberAttribute })\n public step = 1;\n\n constructor() {\n super(0);\n detectControllerChanges(this).subscribe();\n }\n\n public ngAfterViewInit(): void {\n this.destroyDecrementListener = this.pressAndHold(this.decrementButton.nativeElement, () => {\n this.inputElement?.stepDown();\n this.updateValue(numberAttribute(this.inputElement.value));\n });\n this.destroyIncrementListener = this.pressAndHold(this.incrementButton.nativeElement, () => {\n this.inputElement?.stepUp();\n this.updateValue(numberAttribute(this.inputElement.value));\n });\n }\n\n public override ngOnDestroy(): void {\n super.ngOnDestroy();\n this.destroyDecrementListener?.();\n this.destroyIncrementListener?.();\n }\n\n public override updateValue(value: number) {\n if (this.value === value) return;\n super.updateValue(value);\n }\n\n protected maxSteps({ code }: KeyboardEvent) {\n if (code === 'End' && this.min !== -Infinity) {\n this.updateValue(this.min);\n } else if (code === 'Home' && this.max !== Infinity) {\n this.updateValue(this.max);\n }\n }\n\n protected checkAndUpdate(): void {\n const value = numberAttribute(this.inputElement.value);\n const clampedValue = Math.min(this.max, Math.max(this.min, value));\n this.inputElement.value = clampedValue.toString();\n this.updateValue(clampedValue);\n }\n\n private pressAndHold(element: HTMLElement, handler: () => void, interval = 200): () => void {\n let timerId: number | undefined;\n\n const clearTimer = () => {\n clearInterval(timerId);\n timerId = undefined;\n };\n\n const start = () => {\n handler();\n timerId = window.setInterval(handler, interval);\n };\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if ((event.code === 'Space' || event.code === 'Enter') && timerId === undefined) {\n if (event.code === 'Space') {\n event.preventDefault(); // blocks scrolling\n }\n start();\n }\n };\n\n const handleKeyUp = (event: KeyboardEvent) => {\n if (event.code === 'Space' || event.code === 'Enter') {\n clearTimer();\n }\n };\n\n element.addEventListener('mousedown', start);\n element.addEventListener('mouseup', clearTimer);\n element.addEventListener('mouseout', clearTimer);\n element.addEventListener('keydown', handleKeyDown);\n element.addEventListener('keyup', handleKeyUp);\n\n return () => {\n element.removeEventListener('mousedown', start);\n element.removeEventListener('mouseup', clearTimer);\n element.removeEventListener('mouseout', clearTimer);\n element.removeEventListener('keydown', handleKeyDown);\n element.removeEventListener('keyup', handleKeyUp);\n };\n }\n}\n","<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #decrementButton><odx-icon name=\"minus\" /></button>\n<input\n odxControl\n class=\"odx-spinbox__input\"\n [disabled]=\"isDisabled\"\n [readonly]=\"isReadonly\"\n type=\"number\"\n [value]=\"value\"\n [min]=\"min\"\n [max]=\"max\"\n [step]=\"step\"\n (keydown)=\"maxSteps($event)\"\n (change)=\"checkAndUpdate()\"\n (blur)=\"checkAndUpdate()\"\n/>\n<button [disabled]=\"isDisabled || isReadonly\" type=\"button\" class=\"odx-spinbox__action\" #incrementButton><odx-icon name=\"plus\" /></button>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { __decorate, __metadata } from 'tslib';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { InjectionToken, inject, ElementRef,
|
|
4
|
+
import { InjectionToken, inject, ElementRef, input, booleanAttribute, output, Component, ViewEncapsulation, ChangeDetectionStrategy, contentChildren, computed, Injector, viewChild, model, effect, forwardRef, NgModule } from '@angular/core';
|
|
5
5
|
import { RouterLinkActive } from '@angular/router';
|
|
6
6
|
import { DisabledController, CoreModule } from '@odx/angular';
|
|
7
7
|
import { IconComponent } from '@odx/angular/components/icon';
|
|
8
|
-
import { CSSComponent
|
|
9
|
-
import { untilDestroyed, deferFn, injectElement
|
|
10
|
-
import { filter,
|
|
8
|
+
import { CSSComponent } from '@odx/angular/internal';
|
|
9
|
+
import { untilDestroyed, deferFn, injectElement } from '@odx/angular/utils';
|
|
10
|
+
import { filter, merge, tap, skip, distinctUntilChanged } from 'rxjs';
|
|
11
11
|
import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
|
|
12
|
+
import { toObservable } from '@angular/core/rxjs-interop';
|
|
12
13
|
import { ActiveIndicatorDirective } from '@odx/angular/cdk/active-indicator';
|
|
13
|
-
import {
|
|
14
|
+
import { ScrollableService } from '@odx/angular/cdk/scrollable';
|
|
15
|
+
import { fromElementResize$ } from '@odx/angular/rxjs';
|
|
14
16
|
|
|
15
17
|
const TAB_BAR = new InjectionToken('tabBar');
|
|
16
18
|
|
|
@@ -40,13 +42,13 @@ let TabBarItemComponent = class TabBarItemComponent {
|
|
|
40
42
|
* @type {boolean}
|
|
41
43
|
* @default false
|
|
42
44
|
*/
|
|
43
|
-
this.closable = false;
|
|
45
|
+
this.closable = input(false, { transform: booleanAttribute });
|
|
44
46
|
/**
|
|
45
47
|
* Event emitter that fires when the tab is closed. It emits the instance of the tab item component.
|
|
46
48
|
*
|
|
47
49
|
* @emits {TabBarItemComponent}
|
|
48
50
|
*/
|
|
49
|
-
this.tabClose =
|
|
51
|
+
this.tabClose = output();
|
|
50
52
|
}
|
|
51
53
|
get disabled() {
|
|
52
54
|
return !!this.disabledController?.disabled;
|
|
@@ -77,13 +79,13 @@ let TabBarItemComponent = class TabBarItemComponent {
|
|
|
77
79
|
}
|
|
78
80
|
handleClose(event) {
|
|
79
81
|
event.stopPropagation();
|
|
80
|
-
if (this.disabled || !this.closable)
|
|
82
|
+
if (this.disabled || !this.closable())
|
|
81
83
|
return;
|
|
82
84
|
this.tabBar.removeItem(this);
|
|
83
85
|
this.tabClose.emit(this);
|
|
84
86
|
}
|
|
85
87
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabBarItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
86
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: TabBarItemComponent, isStandalone: true, selector: "odx-tab-bar-item", inputs: { closable:
|
|
88
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: TabBarItemComponent, isStandalone: true, selector: "odx-tab-bar-item", inputs: { closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tabClose: "tabClose" }, host: { attributes: { "role": "tab" }, listeners: { "click": "onClick()" }, properties: { "class.is-disabled": "disabled", "class.is-active": "isActive", "attr.aria-selected": "isActive", "class.is-closable": "closable()" } }, providers: [DisabledController.connect()], ngImport: i0, template: "<ng-content select=\"odx-icon\" />\n<div class=\"odx-tab-bar-item__label\"><ng-content /></div>\n@if (closable()) {\n <odx-icon name=\"close\" iconSet=\"core\" size=\"small\" (click)=\"handleClose($event)\" />\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IconComponent, selector: "odx-icon", inputs: ["inline", "size", "name", "iconSet", "identifier"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
87
89
|
};
|
|
88
90
|
TabBarItemComponent = __decorate([
|
|
89
91
|
CSSComponent('tab-bar-item')
|
|
@@ -94,18 +96,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
94
96
|
'[class.is-disabled]': 'disabled',
|
|
95
97
|
'[class.is-active]': 'isActive',
|
|
96
98
|
'[attr.aria-selected]': 'isActive',
|
|
97
|
-
'[class.is-closable]': 'closable',
|
|
99
|
+
'[class.is-closable]': 'closable()',
|
|
98
100
|
role: 'tab',
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
args: [{ transform: booleanAttribute }]
|
|
103
|
-
}], tabClose: [{
|
|
104
|
-
type: Output
|
|
105
|
-
}], onClick: [{
|
|
106
|
-
type: HostListener,
|
|
107
|
-
args: ['click']
|
|
108
|
-
}] } });
|
|
101
|
+
'(click)': 'onClick()',
|
|
102
|
+
}, template: "<ng-content select=\"odx-icon\" />\n<div class=\"odx-tab-bar-item__label\"><ng-content /></div>\n@if (closable()) {\n <odx-icon name=\"close\" iconSet=\"core\" size=\"small\" (click)=\"handleClose($event)\" />\n}\n" }]
|
|
103
|
+
}] });
|
|
109
104
|
|
|
110
105
|
/**
|
|
111
106
|
* `TabBarComponent` creates a dynamic and accessible tab bar interface,
|
|
@@ -119,23 +114,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
119
114
|
*/
|
|
120
115
|
let TabBarComponent = class TabBarComponent {
|
|
121
116
|
constructor() {
|
|
122
|
-
this.changeDetector = inject(ChangeDetectorRef);
|
|
123
117
|
this.takeUntilDestroyed = untilDestroyed();
|
|
124
|
-
this.
|
|
125
|
-
this.
|
|
118
|
+
this.scrollableService = inject(ScrollableService);
|
|
119
|
+
this.nextTab = this.scrollableService.nextItem;
|
|
120
|
+
this.prevTab = this.scrollableService.prevItem;
|
|
121
|
+
this.tabs = contentChildren(TabBarItemComponent);
|
|
122
|
+
this.activeItem = computed(() => this.tabs().at(this.selectedIndex()) ?? null);
|
|
123
|
+
this.keyManager = new ActiveDescendantKeyManager(this.tabs, inject(Injector))
|
|
124
|
+
.withWrap()
|
|
125
|
+
.withHomeAndEnd()
|
|
126
|
+
.withHorizontalOrientation('ltr')
|
|
127
|
+
.withVerticalOrientation(false);
|
|
128
|
+
this.scrollContainerElement = viewChild.required('scrollContainer');
|
|
126
129
|
this.element = injectElement();
|
|
127
130
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*/
|
|
132
|
-
this.overflowLeft = false;
|
|
133
|
-
/**
|
|
134
|
-
* Indicates whether the tab bar has overflowed to the right.
|
|
131
|
+
* Index of the currently selected tab. Use this property to programmatically
|
|
132
|
+
* change the active tab. It responds to changes by updating the active state of the corresponding
|
|
133
|
+
* tab item and adjusting the view if necessary.
|
|
135
134
|
*
|
|
136
|
-
* @type {
|
|
135
|
+
* @type {number}
|
|
136
|
+
* @example
|
|
137
|
+
* Programmatically select the second tab (index starts from 0):
|
|
138
|
+
* ```html
|
|
139
|
+
* <odx-tab-bar [selectedIndex]="1"></odx-tab-bar>
|
|
140
|
+
* ```
|
|
137
141
|
*/
|
|
138
|
-
this.
|
|
142
|
+
this.selectedIndex = model(0);
|
|
139
143
|
/**
|
|
140
144
|
* An EventEmitter that emits whenever a new tab is selected. It provides the `TabChangeEvent`
|
|
141
145
|
* which contains the index of the newly selected tab and the instance of the corresponding
|
|
@@ -153,129 +157,79 @@ let TabBarComponent = class TabBarComponent {
|
|
|
153
157
|
* }
|
|
154
158
|
* ```
|
|
155
159
|
*/
|
|
156
|
-
this.selectedTabChanged =
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
ngAfterViewInit() {
|
|
174
|
-
this.keyManager = new ActiveDescendantKeyManager(this.tabs).withWrap().withHomeAndEnd().withHorizontalOrientation('ltr').withVerticalOrientation(false);
|
|
175
|
-
const updatePanelPosition$ = merge(this.selectedTabChanged, fromQueryList(this.tabs), fromElementResize$(this.element.nativeElement)).pipe(debounceTime(0), tap(() => this.updatePanelPosition()));
|
|
176
|
-
const updateActionVisibility$ = fromEvent(this.tabBarPanelElement.nativeElement, 'transitionend').pipe(tap(({ target }) => target === this.tabBarPanelElement.nativeElement && this.updateActionVisbility()));
|
|
177
|
-
const activeItemChange$ = merge(this.keyManager.change.pipe(tap(() => this.activeItemChange())), this.selectedIndex$$.pipe(tap((index) => this.keyManager?.setActiveItem(index))));
|
|
178
|
-
merge(activeItemChange$, updatePanelPosition$, updateActionVisibility$).pipe(this.takeUntilDestroyed()).subscribe();
|
|
179
|
-
this.tabs.changes.pipe(this.takeUntilDestroyed()).subscribe(() => this.selectedIndex$$.next(this.selectedIndex$$.value));
|
|
160
|
+
this.selectedTabChanged = output();
|
|
161
|
+
const tabState = computed(() => ({
|
|
162
|
+
selectedIndex: this.selectedIndex(),
|
|
163
|
+
selectedTab: this.activeItem(),
|
|
164
|
+
}));
|
|
165
|
+
merge(this.keyManager.change.pipe(tap((index) => this.selectedIndex.set(index ?? -1))), fromElementResize$(this.element.nativeElement, 600).pipe(tap(() => this.scrollableService.scrollActiveItemIntoView(this.activeItem()?.element.nativeElement ?? null))), toObservable(tabState).pipe(skip(1), distinctUntilChanged((a, b) => a.selectedIndex === b.selectedIndex), filter(({ selectedTab }) => !selectedTab?.disabled), tap((state) => {
|
|
166
|
+
this.selectedTabChanged.emit(state);
|
|
167
|
+
this.scrollableService.scrollActiveItemIntoView(state.selectedTab?.element.nativeElement ?? null);
|
|
168
|
+
})))
|
|
169
|
+
.pipe(this.takeUntilDestroyed())
|
|
170
|
+
.subscribe();
|
|
171
|
+
effect(() => {
|
|
172
|
+
this.keyManager.setActiveItem(this.selectedIndex());
|
|
173
|
+
}, { allowSignalWrites: true });
|
|
174
|
+
effect(() => {
|
|
175
|
+
this.scrollableService.observe(this.scrollContainerElement(), this.tabs());
|
|
176
|
+
});
|
|
180
177
|
}
|
|
181
178
|
/**
|
|
182
|
-
* Activates the specified tab item.
|
|
179
|
+
* Activates the specified tab item, making it the currently selected tab.
|
|
180
|
+
* If the item is already active, no action is taken.
|
|
183
181
|
*
|
|
184
|
-
* @param {TabBarItemComponent | null} item - The tab item to activate.
|
|
185
|
-
*
|
|
186
|
-
* @example
|
|
187
|
-
* ```ts
|
|
188
|
-
* // Activate the first tab item
|
|
189
|
-
* tabBar.activateItem(tabItems[0]);
|
|
190
|
-
*
|
|
191
|
-
* // Clear the active item
|
|
192
|
-
* tabBar.activateItem(null);
|
|
193
|
-
* ```
|
|
182
|
+
* @param {TabBarItemComponent | null} item - The tab item to activate.
|
|
194
183
|
*/
|
|
195
184
|
activateItem(item) {
|
|
196
|
-
if (item === this.activeItem)
|
|
185
|
+
if (item === this.keyManager?.activeItem)
|
|
197
186
|
return;
|
|
198
|
-
const selectedIndex = item ? this.tabs
|
|
199
|
-
this.
|
|
187
|
+
const selectedIndex = item ? this.tabs().indexOf(item) : -1;
|
|
188
|
+
this.selectedIndex.set(selectedIndex);
|
|
200
189
|
}
|
|
201
190
|
/**
|
|
202
|
-
*
|
|
203
|
-
* If the
|
|
191
|
+
* Fires when the specified tab item is removed from the tab bar.
|
|
192
|
+
* If the item is currently active, the previous tab is activated.
|
|
204
193
|
*
|
|
205
|
-
* @param {TabBarItemComponent} item - The item to
|
|
194
|
+
* @param {TabBarItemComponent} item - The tab item to remove.
|
|
206
195
|
*/
|
|
207
196
|
removeItem(item) {
|
|
208
|
-
if (this.activeItem !== item)
|
|
197
|
+
if (this.keyManager?.activeItem !== item)
|
|
209
198
|
return;
|
|
210
|
-
|
|
199
|
+
this.keyManager?.setPreviousItemActive();
|
|
211
200
|
}
|
|
212
201
|
onKeydown(event) {
|
|
213
202
|
this.keyManager?.onKeydown(event);
|
|
214
203
|
}
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
if (!
|
|
204
|
+
onFocused() {
|
|
205
|
+
const activeItem = this.activeItem()?.element.nativeElement;
|
|
206
|
+
if (!activeItem)
|
|
218
207
|
return;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const panelPositionX = tabBarRect.width / 2 - activeElement.offsetLeft - activeElement.offsetWidth / 2;
|
|
223
|
-
const positionX = Math.min(0, Math.max(panelPositionX, maxPanelPositionX));
|
|
224
|
-
applyStyles(this.tabBarPanelElement.nativeElement, {
|
|
225
|
-
transform: cssTranslate(px(positionX), 0),
|
|
226
|
-
});
|
|
227
|
-
this.updateActionVisbility();
|
|
228
|
-
}
|
|
229
|
-
updateActionVisbility() {
|
|
230
|
-
const panelRect = this.tabBarPanelElement.nativeElement.getBoundingClientRect();
|
|
231
|
-
const tabBarRect = this.element.nativeElement.getBoundingClientRect();
|
|
232
|
-
this.overflowLeft = panelRect.left < tabBarRect.left;
|
|
233
|
-
this.overflowRight = panelRect.right > tabBarRect.right;
|
|
234
|
-
this.changeDetector.detectChanges();
|
|
208
|
+
if (this.scrollableService.visibleItems().includes(activeItem))
|
|
209
|
+
return;
|
|
210
|
+
this.scrollableService.scrollActiveItemIntoView(activeItem);
|
|
235
211
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
this.activeItem = activeItem ?? null;
|
|
239
|
-
this.selectedTabChanged.emit({ selectedIndex: activeItemIndex ?? -1, selectedTab: this.keyManager?.activeItem ?? null });
|
|
212
|
+
scrollTabs(direction = 1) {
|
|
213
|
+
this.scrollableService.scroll(direction);
|
|
240
214
|
}
|
|
241
215
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
242
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
216
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.2.13", type: TabBarComponent, isStandalone: true, selector: "odx-tab-bar", inputs: { selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndex: "selectedIndexChange", selectedTabChanged: "selectedTabChanged" }, host: { attributes: { "role": "tabpanel", "tabindex": "0" }, listeners: { "keydown": "onKeydown($event)", "focus": "onFocused()" }, properties: { "class.odx-tab-bar--overflow-left": "!!prevTab()", "class.odx-tab-bar--overflow-right": "!!nextTab()" } }, providers: [{ provide: TAB_BAR, useExisting: forwardRef(() => TabBarComponent) }, ScrollableService], queries: [{ propertyName: "tabs", predicate: TabBarItemComponent, isSignal: true }], viewQueries: [{ propertyName: "scrollContainerElement", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<button #tabAction class=\"odx-tab-bar__action odx-tab-bar__action--prev\" [class.is-active]=\"!!prevTab()\" (click)=\"scrollTabs(-1)\" tabindex=\"-1\">\n <odx-icon class=\"tab-bar-action-icon\" name=\"chevron-left\" iconSet=\"core\" />\n</button>\n<div class=\"odx-tab-bar__inner\" #scrollContainer tabindex=\"-1\">\n <div class=\"odx-tab-bar__panel\" role=\"tablist\" #tabBarPanel>\n <ng-content />\n <div class=\"odx-tab-bar__indicator\" [odxActiveIndicator]=\"activeItem()?.element?.nativeElement\" odxActiveIndicatorPosition=\"start\"></div>\n </div>\n</div>\n<button #tabAction class=\"odx-tab-bar__action odx-tab-bar__action--next\" [class.is-active]=\"!!nextTab()\" (click)=\"scrollTabs()\" tabindex=\"-1\">\n <odx-icon class=\"tab-bar-action-icon\" name=\"chevron-right\" iconSet=\"core\" />\n</button>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IconComponent, selector: "odx-icon", inputs: ["inline", "size", "name", "iconSet", "identifier"] }, { kind: "directive", type: ActiveIndicatorDirective, selector: "[odxActiveIndicator]", inputs: ["odxActiveIndicator", "odxActiveIndicatorParent", "odxActiveIndicatorDirection", "odxActiveIndicatorPosition"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
243
217
|
};
|
|
244
|
-
__decorate([
|
|
245
|
-
CSSModifier(),
|
|
246
|
-
__metadata("design:type", Object)
|
|
247
|
-
], TabBarComponent.prototype, "overflowLeft", void 0);
|
|
248
|
-
__decorate([
|
|
249
|
-
CSSModifier(),
|
|
250
|
-
__metadata("design:type", Object)
|
|
251
|
-
], TabBarComponent.prototype, "overflowRight", void 0);
|
|
252
218
|
TabBarComponent = __decorate([
|
|
253
|
-
CSSComponent('tab-bar')
|
|
219
|
+
CSSComponent('tab-bar'),
|
|
220
|
+
__metadata("design:paramtypes", [])
|
|
254
221
|
], TabBarComponent);
|
|
255
222
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabBarComponent, decorators: [{
|
|
256
223
|
type: Component,
|
|
257
|
-
args: [{ selector: 'odx-tab-bar', standalone: true, imports: [CommonModule, IconComponent, ActiveIndicatorDirective], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: TAB_BAR, useExisting: forwardRef(() => TabBarComponent) }], host: {
|
|
224
|
+
args: [{ selector: 'odx-tab-bar', standalone: true, imports: [CommonModule, IconComponent, ActiveIndicatorDirective], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: TAB_BAR, useExisting: forwardRef(() => TabBarComponent) }, ScrollableService], host: {
|
|
258
225
|
role: 'tabpanel',
|
|
259
226
|
tabindex: '0',
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
args: ['tabAction']
|
|
267
|
-
}], tabs: [{
|
|
268
|
-
type: ContentChildren,
|
|
269
|
-
args: [TabBarItemComponent]
|
|
270
|
-
}], overflowLeft: [], overflowRight: [], selectedIndex: [{
|
|
271
|
-
type: Input,
|
|
272
|
-
args: [{ transform: numberAttribute }]
|
|
273
|
-
}], selectedTabChanged: [{
|
|
274
|
-
type: Output
|
|
275
|
-
}], onKeydown: [{
|
|
276
|
-
type: HostListener,
|
|
277
|
-
args: ['keydown', ['$event']]
|
|
278
|
-
}] } });
|
|
227
|
+
'[class.odx-tab-bar--overflow-left]': '!!prevTab()',
|
|
228
|
+
'[class.odx-tab-bar--overflow-right]': '!!nextTab()',
|
|
229
|
+
'(keydown)': 'onKeydown($event)',
|
|
230
|
+
'(focus)': 'onFocused()',
|
|
231
|
+
}, template: "<button #tabAction class=\"odx-tab-bar__action odx-tab-bar__action--prev\" [class.is-active]=\"!!prevTab()\" (click)=\"scrollTabs(-1)\" tabindex=\"-1\">\n <odx-icon class=\"tab-bar-action-icon\" name=\"chevron-left\" iconSet=\"core\" />\n</button>\n<div class=\"odx-tab-bar__inner\" #scrollContainer tabindex=\"-1\">\n <div class=\"odx-tab-bar__panel\" role=\"tablist\" #tabBarPanel>\n <ng-content />\n <div class=\"odx-tab-bar__indicator\" [odxActiveIndicator]=\"activeItem()?.element?.nativeElement\" odxActiveIndicatorPosition=\"start\"></div>\n </div>\n</div>\n<button #tabAction class=\"odx-tab-bar__action odx-tab-bar__action--next\" [class.is-active]=\"!!nextTab()\" (click)=\"scrollTabs()\" tabindex=\"-1\">\n <odx-icon class=\"tab-bar-action-icon\" name=\"chevron-right\" iconSet=\"core\" />\n</button>\n" }]
|
|
232
|
+
}], ctorParameters: () => [] });
|
|
279
233
|
|
|
280
234
|
const modules = [TabBarComponent, TabBarItemComponent];
|
|
281
235
|
class TabBarModule {
|