@ngrdt/tabs 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @ngrdt/tabs
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Running unit tests
6
+
7
+ Run `nx test @ngrdt/tabs` to execute the unit tests.
@@ -0,0 +1,44 @@
1
+ const nx = require('@nx/eslint-plugin');
2
+ const baseConfig = require('../../eslint.config.js');
3
+
4
+ module.exports = [
5
+ ...baseConfig,
6
+ {
7
+ files: ['**/*.json'],
8
+ rules: {
9
+ '@nx/dependency-checks': [
10
+ 'error',
11
+ { ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'] },
12
+ ],
13
+ },
14
+ languageOptions: { parser: require('jsonc-eslint-parser') },
15
+ },
16
+ ...nx.configs['flat/angular'],
17
+ ...nx.configs['flat/angular-template'],
18
+ {
19
+ files: ['**/*.ts'],
20
+ rules: {
21
+ '@angular-eslint/directive-selector': [
22
+ 'error',
23
+ {
24
+ type: 'attribute',
25
+ prefix: 'lib',
26
+ style: 'camelCase',
27
+ },
28
+ ],
29
+ '@angular-eslint/component-selector': [
30
+ 'error',
31
+ {
32
+ type: 'element',
33
+ prefix: 'lib',
34
+ style: 'kebab-case',
35
+ },
36
+ ],
37
+ },
38
+ },
39
+ {
40
+ files: ['**/*.html'],
41
+ // Override or add rules here
42
+ rules: {},
43
+ },
44
+ ];
package/jest.config.ts ADDED
@@ -0,0 +1,21 @@
1
+ export default {
2
+ displayName: '@ngrdt/tabs',
3
+ preset: '../../jest.preset.js',
4
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
5
+ coverageDirectory: '../../coverage/@ngrdt/tabs',
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': [
8
+ 'jest-preset-angular',
9
+ {
10
+ tsconfig: '<rootDir>/tsconfig.spec.json',
11
+ stringifyContentPathRegex: '\\.(html|svg)$',
12
+ },
13
+ ],
14
+ },
15
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
16
+ snapshotSerializers: [
17
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
18
+ 'jest-preset-angular/build/serializers/ng-snapshot',
19
+ 'jest-preset-angular/build/serializers/html-comment',
20
+ ],
21
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/@ngrdt/tabs",
4
+ "lib": {
5
+ "entryFile": "src/index.ts"
6
+ }
7
+ }
package/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@ngrdt/tabs",
3
+ "version": "0.0.1",
4
+ "peerDependencies": {
5
+ "@angular/common": "^18.2.0",
6
+ "@angular/core": "^18.2.0"
7
+ },
8
+ "sideEffects": false
9
+ }
package/project.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@ngrdt/tabs",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "@ngrdt/tabs/src",
5
+ "prefix": "lib",
6
+ "projectType": "library",
7
+ "tags": [],
8
+ "targets": {
9
+ "build": {
10
+ "executor": "@nx/angular:package",
11
+ "outputs": ["{workspaceRoot}/dist/{projectRoot}"],
12
+ "options": {
13
+ "project": "@ngrdt/tabs/ng-package.json"
14
+ },
15
+ "configurations": {
16
+ "production": {
17
+ "tsConfig": "@ngrdt/tabs/tsconfig.lib.prod.json"
18
+ },
19
+ "development": {
20
+ "tsConfig": "@ngrdt/tabs/tsconfig.lib.json"
21
+ }
22
+ },
23
+ "defaultConfiguration": "production"
24
+ },
25
+ "test": {
26
+ "executor": "@nx/jest:jest",
27
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
28
+ "options": {
29
+ "jestConfig": "@ngrdt/tabs/jest.config.ts"
30
+ }
31
+ },
32
+ "lint": {
33
+ "executor": "@nx/eslint:lint"
34
+ }
35
+ }
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './lib/components/tab-container/rdt-tab-container.component';
2
+ export * from './lib/components/tab/rdt-tab.component';
3
+ export * from './lib/directives/rdt-destroy-inactive.directive';
4
+ export * from './lib/directives/rdt-tab-controller.directive';
5
+ export * from './lib/rdt-tabs-models';
6
+ export * from './lib/rdt-tabs.module';
7
+ export * from './lib/strategies/rdt-tabs-shortcut-strategy';
8
+ export * from './lib/strategies/static-rdt-tabs-shortcut-strategy';
@@ -0,0 +1,3 @@
1
+ <ng-template>
2
+ <ng-content></ng-content>
3
+ </ng-template>
@@ -0,0 +1,158 @@
1
+ import {
2
+ booleanAttribute,
3
+ ChangeDetectionStrategy,
4
+ Component,
5
+ ContentChild,
6
+ DestroyRef,
7
+ inject,
8
+ Injector,
9
+ Input,
10
+ OnChanges,
11
+ OnInit,
12
+ SimpleChanges,
13
+ TemplateRef,
14
+ ViewChild,
15
+ } from '@angular/core';
16
+ import { RDT_GUARDED_COMPONENT, RdtContainerDirective } from '@ngrdt/core';
17
+ import { Nullable } from '@ngrdt/utils';
18
+ import { RdtDestroyInactiveDirective } from '../../directives/rdt-destroy-inactive.directive';
19
+ import { RdtTabContainerState, RdtTabState } from '../../rdt-tabs-models';
20
+ import { RdtTabContainerComponent } from '../tab-container/rdt-tab-container.component';
21
+
22
+ @Component({
23
+ selector: 'rdt-tab',
24
+ templateUrl: './rdt-tab.component.html',
25
+ changeDetection: ChangeDetectionStrategy.OnPush,
26
+ providers: [
27
+ {
28
+ provide: RDT_GUARDED_COMPONENT,
29
+ useExisting: RdtTabComponent,
30
+ },
31
+ ],
32
+ hostDirectives: [RdtContainerDirective],
33
+ })
34
+ export class RdtTabComponent implements OnChanges, OnInit {
35
+ private readonly injector = inject(Injector);
36
+ readonly destroyRef = inject(DestroyRef);
37
+
38
+ private _contDir!: RdtContainerDirective;
39
+ get contDir() {
40
+ return this._contDir;
41
+ }
42
+
43
+ @Input()
44
+ get stateId() {
45
+ if (this._stateId === null) {
46
+ if (this.label !== '') {
47
+ this._stateId = this.label;
48
+ } else {
49
+ const index = this.tabContainer?.tabsList?.toArray().indexOf(this);
50
+ this._stateId = `tab-${index}`;
51
+ }
52
+ }
53
+ return this._stateId;
54
+ }
55
+ set stateId(value: string) {
56
+ this._stateId = value;
57
+ }
58
+ private _stateId: string = null as unknown as string;
59
+
60
+ @Input({ transform: booleanAttribute })
61
+ disabled = false;
62
+
63
+ @Input({ transform: booleanAttribute })
64
+ visible = true;
65
+
66
+ @Input({ required: true })
67
+ label!: string;
68
+
69
+ @Input({ transform: booleanAttribute })
70
+ formAutofocus = true;
71
+
72
+ @Input()
73
+ buttonClass: Nullable<string> = null;
74
+
75
+ @ViewChild(TemplateRef, { static: true })
76
+ private readonly implicitContent!: TemplateRef<any>;
77
+
78
+ @ContentChild(RdtDestroyInactiveDirective, {
79
+ read: TemplateRef,
80
+ static: true,
81
+ })
82
+ private readonly explicitContent!: TemplateRef<any>;
83
+
84
+ get templateRef() {
85
+ return this.explicitContent ?? this.implicitContent;
86
+ }
87
+
88
+ readonly tabContainer = inject(RdtTabContainerComponent);
89
+
90
+ //get forms() {
91
+ // return this.getChildrenByClass(RdtBaseFormComponent);
92
+ //}
93
+
94
+ get childContainers() {
95
+ return this.contDir.getChildrenByClass(RdtTabContainerComponent);
96
+ }
97
+
98
+ ngOnInit() {
99
+ this._contDir = this.injector.get(RdtContainerDirective);
100
+ }
101
+
102
+ ngOnChanges(changes: SimpleChanges) {
103
+ if (
104
+ 'label' in changes ||
105
+ 'value' in changes ||
106
+ 'visible' in changes ||
107
+ 'stateId' in changes
108
+ ) {
109
+ this.tabContainer.cd.markForCheck();
110
+ }
111
+ }
112
+
113
+ get isTopLevel() {
114
+ return this.tabContainer.isTopLevel;
115
+ }
116
+
117
+ getState(): RdtTabState {
118
+ return {
119
+ id: this.stateId,
120
+ containerStates: this.childContainers.map((cont) => cont.getState()),
121
+ };
122
+ }
123
+
124
+ applyState(state: RdtTabState) {
125
+ const states: (RdtTabContainerState | null)[] = [...state.containerStates];
126
+ const conts: (RdtTabContainerComponent | null)[] = this.childContainers;
127
+ const minLen = Math.min(states.length, conts.length);
128
+
129
+ for (let i = 0; i < minLen; i++) {
130
+ if (conts[i]!.stateId === states[i]!.id) {
131
+ conts[i]!.applyState(states[i]!);
132
+ conts[i] = states[i] = null;
133
+ }
134
+ }
135
+ conts.forEach((cont, contI) => {
136
+ if (!cont) {
137
+ return;
138
+ }
139
+ for (let offset = 0; offset < states.length; offset++) {
140
+ const prev = contI - offset;
141
+ const next = contI + offset;
142
+ if (prev > 0 && states[prev]?.id === cont.stateId) {
143
+ cont.applyState(states[prev]!);
144
+ states[prev] = null;
145
+ return;
146
+ } else if (next < states.length && states[next]?.id === cont.stateId) {
147
+ cont.applyState(states[next]!);
148
+ states[next] = null;
149
+ return;
150
+ }
151
+ }
152
+ });
153
+ }
154
+
155
+ focusForm() {
156
+ //TODO: getRdtAutofocusable(this.forms)?.rdtFocus();
157
+ }
158
+ }
@@ -0,0 +1,29 @@
1
+ <div class="tab-header-panel" *ngIf="tabsList">
2
+ <button
3
+ *ngFor="let tab of tabsList"
4
+ #headerButton
5
+ class="px-2 py-1 tab-header-button"
6
+ (click)="activateTab(tab.stateId)"
7
+ [class.active]="value === tab.stateId"
8
+ [disabled]="tab.disabled"
9
+ [hidden]="!tab.visible"
10
+ role="tab"
11
+ [attr.aria-selected]="value === tab.stateId"
12
+ [attr.aria-controls]="'panel-' + tab.stateId"
13
+ [attr.tabindex]="value === tab.stateId ? 0 : -1"
14
+ [id]="'tab-' + tab.stateId"
15
+ [class]="tab.buttonClass"
16
+ type="button"
17
+ >
18
+ {{ tab.label }}
19
+ </button>
20
+ </div>
21
+ <div
22
+ class="tab-content"
23
+ role="tabpanel"
24
+ [id]="'panel-' + value"
25
+ [attr.aria-labelledby]="'tab-' + value"
26
+ #tabContent
27
+ >
28
+ <ng-template [ngTemplateOutlet]="currentTemplate"></ng-template>
29
+ </div>
@@ -0,0 +1,155 @@
1
+ dp3-tab-container {
2
+ --tab-header-border-height: 2px;
3
+ --tab-header-border-color: #e2e8f0;
4
+ --tab-text-color: rgb(100, 116, 139);
5
+ --active-tab-text-color: var(--primary-blue);
6
+ --active-tab-border-color: var(--primary-blue);
7
+ --disabled-tab-text-color: #b1b9c5;
8
+ --tab-vertical-button-width: 200px;
9
+
10
+ display: block;
11
+ position: relative;
12
+
13
+ &.active > .tab-header-panel {
14
+ background-color: var(--hover-blue);
15
+ }
16
+
17
+ &.panel-vertical {
18
+ display: flex;
19
+
20
+ > .tab-header-panel {
21
+ position: absolute;
22
+ top: 0;
23
+ height: 100%;
24
+ width: var(--tab-vertical-button-width);
25
+ overflow-y: auto;
26
+
27
+ > button {
28
+ text-align: left;
29
+ width: 100%;
30
+ white-space: nowrap;
31
+ overflow-x: hidden;
32
+ text-overflow: ellipsis;
33
+ }
34
+ }
35
+
36
+ > .tab-content {
37
+ width: calc(100% - var(--tab-vertical-button-width));
38
+ }
39
+ }
40
+
41
+ &.panel-right {
42
+ flex-direction: row-reverse;
43
+ > .tab-header-panel {
44
+ right: 0;
45
+ }
46
+ }
47
+
48
+ &.panel-left {
49
+ > .tab-header-panel {
50
+ left: 0;
51
+ }
52
+
53
+ > .tab-content {
54
+ margin-left: var(--tab-vertical-button-width);
55
+ }
56
+ }
57
+
58
+ &.panel-horizontal {
59
+ > .tab-header-panel {
60
+ width: 100%;
61
+ white-space: nowrap;
62
+ overflow-x: auto;
63
+ }
64
+ }
65
+
66
+ &.panel-bottom {
67
+ display: flex;
68
+ flex-direction: column-reverse;
69
+ }
70
+
71
+ .tab-header-panel {
72
+ position: relative;
73
+ background-color: rgba(var(--primary-blue-rgb), 0.045);
74
+
75
+ &::after {
76
+ content: '';
77
+ position: absolute;
78
+ width: 100%;
79
+ height: var(--tab-header-border-height);
80
+ background-color: var(--tab-header-border-color);
81
+ margin-top: var(--tab-header-border-height);
82
+ bottom: 0;
83
+ left: 0;
84
+ z-index: -1;
85
+ }
86
+ }
87
+
88
+ .dot {
89
+ position: absolute;
90
+ //min-width: 1.1rem;
91
+ width: 0.85rem; //zakomentovat
92
+ height: 0.85rem; //zakomentovat
93
+ padding: 0 0.1rem;
94
+ background-color: var(--primary-blue);
95
+ color: var(--white);
96
+ font-size: x-small;
97
+ border-radius: 100%;
98
+ top: 0.25rem;
99
+ right: 0rem;
100
+ }
101
+
102
+ .tab-header-button {
103
+ position: relative;
104
+ background: none;
105
+ font: inherit;
106
+ outline: inherit;
107
+ border: none;
108
+ color: var(--tab-text-color);
109
+ font-weight: 700;
110
+ font-size: 1.1rem;
111
+
112
+ &.active {
113
+ position: relative;
114
+ color: var(--active-tab-text-color);
115
+
116
+ &::after {
117
+ content: '';
118
+ position: absolute;
119
+ width: 100%;
120
+ height: var(--tab-header-border-height);
121
+ background-color: var(--active-tab-text-color);
122
+ margin-top: var(--tab-header-border-height);
123
+ bottom: 0;
124
+ left: 0;
125
+ }
126
+ }
127
+
128
+ &:disabled {
129
+ color: var(--disabled-tab-text-color);
130
+ }
131
+
132
+ &.padding-small {
133
+ padding-right: 1.1rem !important;
134
+ }
135
+
136
+ &.padding-large {
137
+ padding-right: 1.3rem !important;
138
+ }
139
+ }
140
+
141
+ > .tab-content {
142
+ min-width: 0;
143
+ width: 100%;
144
+ padding: 0.5rem;
145
+
146
+ &:has(> .dp3-tab-container) {
147
+ padding: 0;
148
+ }
149
+
150
+ > .dp3-tab-container {
151
+ width: 100%;
152
+ height: 100%;
153
+ }
154
+ }
155
+ }