ngx-opalbytes-components 1.19.0 → 1.21.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.
Files changed (95) hide show
  1. package/README.md +103 -0
  2. package/cli/cao-comp.cjs +456 -0
  3. package/fesm2022/ngx-opalbytes-components.mjs +23 -7
  4. package/fesm2022/ngx-opalbytes-components.mjs.map +1 -1
  5. package/package.json +4 -1
  6. package/src/lib/shared/components/autocomplete/autocomplete.component.html +126 -0
  7. package/src/lib/shared/components/autocomplete/autocomplete.component.scss +150 -0
  8. package/src/lib/shared/components/autocomplete/autocomplete.component.ts +552 -0
  9. package/src/lib/shared/components/autocomplete/autocomplete.stories.ts +133 -0
  10. package/src/lib/shared/components/base-button/base-button.component.html +33 -0
  11. package/src/lib/shared/components/base-button/base-button.component.scss +14 -0
  12. package/src/lib/shared/components/base-button/base-button.component.spec.ts +64 -0
  13. package/src/lib/shared/components/base-button/base-button.component.ts +48 -0
  14. package/src/lib/shared/components/base-button/base-button.stories.ts +102 -0
  15. package/src/lib/shared/components/base-icon/base-icon.css +0 -0
  16. package/src/lib/shared/components/base-icon/base-icon.html +11 -0
  17. package/src/lib/shared/components/base-icon/base-icon.spec.ts +23 -0
  18. package/src/lib/shared/components/base-icon/base-icon.stories.ts +56 -0
  19. package/src/lib/shared/components/base-icon/base-icon.ts +18 -0
  20. package/src/lib/shared/components/base-input/base-input.css +193 -0
  21. package/src/lib/shared/components/base-input/base-input.html +43 -0
  22. package/src/lib/shared/components/base-input/base-input.spec.ts +146 -0
  23. package/src/lib/shared/components/base-input/base-input.stories.ts +137 -0
  24. package/src/lib/shared/components/base-input/base-input.ts +133 -0
  25. package/src/lib/shared/components/base-time-range/base-time-range.css +27 -0
  26. package/src/lib/shared/components/base-time-range/base-time-range.html +23 -0
  27. package/src/lib/shared/components/base-time-range/base-time-range.spec.ts +27 -0
  28. package/src/lib/shared/components/base-time-range/base-time-range.stories.ts +54 -0
  29. package/src/lib/shared/components/base-time-range/base-time-range.ts +61 -0
  30. package/src/lib/shared/components/base-toggle/base-toggle.component.html +28 -0
  31. package/src/lib/shared/components/base-toggle/base-toggle.component.scss +45 -0
  32. package/src/lib/shared/components/base-toggle/base-toggle.component.spec.ts +22 -0
  33. package/src/lib/shared/components/base-toggle/base-toggle.component.ts +138 -0
  34. package/src/lib/shared/components/base-toggle/base-toggle.stories.ts +63 -0
  35. package/src/lib/shared/components/card/card.component.css +264 -0
  36. package/src/lib/shared/components/card/card.component.html +103 -0
  37. package/src/lib/shared/components/card/card.component.ts +63 -0
  38. package/src/lib/shared/components/card/card.stories.ts +114 -0
  39. package/src/lib/shared/components/date-picker/date-picker.html +82 -0
  40. package/src/lib/shared/components/date-picker/date-picker.scss +8 -0
  41. package/src/lib/shared/components/date-picker/date-picker.spec.ts +23 -0
  42. package/src/lib/shared/components/date-picker/date-picker.stories.ts +91 -0
  43. package/src/lib/shared/components/date-picker/date-picker.ts +165 -0
  44. package/src/lib/shared/components/drop-down/drop-down.css +61 -0
  45. package/src/lib/shared/components/drop-down/drop-down.html +96 -0
  46. package/src/lib/shared/components/drop-down/drop-down.spec.ts +335 -0
  47. package/src/lib/shared/components/drop-down/drop-down.stories.ts +90 -0
  48. package/src/lib/shared/components/drop-down/drop-down.ts +335 -0
  49. package/src/lib/shared/components/footer/footer.css +29 -0
  50. package/src/lib/shared/components/footer/footer.html +10 -0
  51. package/src/lib/shared/components/footer/footer.spec.ts +22 -0
  52. package/src/lib/shared/components/footer/footer.stories.ts +51 -0
  53. package/src/lib/shared/components/footer/footer.ts +15 -0
  54. package/src/lib/shared/components/index.ts +19 -0
  55. package/src/lib/shared/components/kpi/kpi.css +0 -0
  56. package/src/lib/shared/components/kpi/kpi.html +11 -0
  57. package/src/lib/shared/components/kpi/kpi.spec.ts +38 -0
  58. package/src/lib/shared/components/kpi/kpi.stories.ts +65 -0
  59. package/src/lib/shared/components/kpi/kpi.ts +25 -0
  60. package/src/lib/shared/components/links-button/links-button.css +70 -0
  61. package/src/lib/shared/components/links-button/links-button.html +23 -0
  62. package/src/lib/shared/components/links-button/links-button.spec.ts +139 -0
  63. package/src/lib/shared/components/links-button/links-button.stories.ts +75 -0
  64. package/src/lib/shared/components/links-button/links-button.ts +45 -0
  65. package/src/lib/shared/components/paginator/paginator.css +0 -0
  66. package/src/lib/shared/components/paginator/paginator.html +10 -0
  67. package/src/lib/shared/components/paginator/paginator.spec.ts +23 -0
  68. package/src/lib/shared/components/paginator/paginator.stories.ts +56 -0
  69. package/src/lib/shared/components/paginator/paginator.ts +25 -0
  70. package/src/lib/shared/components/stepper/step/step.css +8 -0
  71. package/src/lib/shared/components/stepper/step/step.html +1 -0
  72. package/src/lib/shared/components/stepper/step/step.spec.ts +131 -0
  73. package/src/lib/shared/components/stepper/step/step.ts +14 -0
  74. package/src/lib/shared/components/stepper/stepper.css +17 -0
  75. package/src/lib/shared/components/stepper/stepper.html +11 -0
  76. package/src/lib/shared/components/stepper/stepper.spec.ts +238 -0
  77. package/src/lib/shared/components/stepper/stepper.stories.ts +77 -0
  78. package/src/lib/shared/components/stepper/stepper.ts +57 -0
  79. package/src/lib/shared/components/tab-group/tab-group.component.html +84 -0
  80. package/src/lib/shared/components/tab-group/tab-group.component.scss +151 -0
  81. package/src/lib/shared/components/tab-group/tab-group.spec.ts +22 -0
  82. package/src/lib/shared/components/tab-group/tab-group.stories.ts +102 -0
  83. package/src/lib/shared/components/tab-group/tab-group.ts +218 -0
  84. package/src/lib/shared/components/tab-panel/tab-content.directive.ts +7 -0
  85. package/src/lib/shared/components/tab-panel/tab-label.directive.ts +7 -0
  86. package/src/lib/shared/components/tab-panel/tab-panel.css +0 -0
  87. package/src/lib/shared/components/tab-panel/tab-panel.spec.ts +22 -0
  88. package/src/lib/shared/components/tab-panel/tab-panel.stories.ts +54 -0
  89. package/src/lib/shared/components/tab-panel/tab-panel.ts +31 -0
  90. package/src/lib/shared/components/time-picker/time-picker.css +84 -0
  91. package/src/lib/shared/components/time-picker/time-picker.html +21 -0
  92. package/src/lib/shared/components/time-picker/time-picker.spec.ts +23 -0
  93. package/src/lib/shared/components/time-picker/time-picker.stories.ts +66 -0
  94. package/src/lib/shared/components/time-picker/time-picker.ts +90 -0
  95. package/types/ngx-opalbytes-components.d.ts +23 -4
@@ -0,0 +1,238 @@
1
+ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
2
+ import { ComponentFixture, TestBed } from "@angular/core/testing";
3
+ import { StepperComponent } from "./stepper";
4
+ import { StepComponent } from "./step/step";
5
+ import { Component, QueryList, EventEmitter } from "@angular/core";
6
+ import { By } from "@angular/platform-browser";
7
+
8
+ describe("StepperComponent", () => {
9
+ let component: StepperComponent;
10
+ let fixture: ComponentFixture<StepperComponent>;
11
+
12
+ @Component({
13
+ selector: "cao-step",
14
+ template: "<div></div>",
15
+ standalone: true,
16
+ })
17
+ class MockStepComponent {
18
+ isActive = false;
19
+ }
20
+
21
+ beforeEach(async () => {
22
+ await TestBed.configureTestingModule({
23
+ imports: [StepperComponent, MockStepComponent],
24
+ })
25
+ .overrideComponent(StepperComponent, {
26
+ remove: { imports: [StepComponent] },
27
+ add: { imports: [MockStepComponent] },
28
+ })
29
+ .compileComponents();
30
+
31
+ fixture = TestBed.createComponent(StepperComponent);
32
+ component = fixture.componentInstance;
33
+ });
34
+
35
+ afterEach(() => {
36
+ vi.clearAllMocks();
37
+ });
38
+
39
+ it("deve criar o componente", () => {
40
+ expect(component).toBeTruthy();
41
+ });
42
+
43
+ describe("ngAfterContentInit", () => {
44
+ it("deve ativar o primeiro passo e definir lengthStep quando há passos", () => {
45
+ const step1 = new MockStepComponent();
46
+ const step2 = new MockStepComponent();
47
+ component.steps = new QueryList<StepComponent>();
48
+ component.steps.reset([step1 as StepComponent, step2 as StepComponent]);
49
+
50
+ component.ngAfterContentInit();
51
+
52
+ expect(component.steps.toArray()[0].isActive).toBe(true);
53
+ expect(component.lengthStep).toBe(2);
54
+ });
55
+
56
+ it("não deve fazer nada quando não há passos", () => {
57
+ component.steps = new QueryList<StepComponent>();
58
+ component.steps.reset([]);
59
+ const originalLength = component.lengthStep;
60
+
61
+ component.ngAfterContentInit();
62
+
63
+ expect(component.lengthStep).toBe(originalLength);
64
+ });
65
+ });
66
+
67
+ describe("nextStep", () => {
68
+ it("deve avançar para o próximo passo quando não estiver no último", () => {
69
+ const steps = [
70
+ { isActive: true } as StepComponent,
71
+ { isActive: false } as StepComponent,
72
+ { isActive: false } as StepComponent,
73
+ ];
74
+ component.steps = new QueryList<StepComponent>();
75
+ component.steps.reset(steps);
76
+ component.activeStepIndex = 0;
77
+
78
+ const emitSpy = vi.spyOn(component.maxReachedEvent as EventEmitter<null>, 'emit');
79
+
80
+ component.nextStep();
81
+
82
+ expect(component.activeStepIndex).toBe(1);
83
+ expect(steps[1].isActive).toBe(true);
84
+ expect(emitSpy).not.toHaveBeenCalled();
85
+ });
86
+
87
+ it("deve emitir maxReachedEvent quando estiver no último passo", () => {
88
+ const steps = [
89
+ { isActive: true } as StepComponent,
90
+ { isActive: true } as StepComponent
91
+ ];
92
+ component.steps = new QueryList<StepComponent>();
93
+ component.steps.reset(steps);
94
+ component.activeStepIndex = 1;
95
+
96
+ const emitSpy = vi.spyOn(component.maxReachedEvent as EventEmitter<null>, 'emit');
97
+
98
+ component.nextStep();
99
+
100
+ expect(emitSpy).toHaveBeenCalledWith(null);
101
+ expect(component.activeStepIndex).toBe(1);
102
+ });
103
+
104
+ it("não deve fazer nada quando não há passos", () => {
105
+ component.steps = new QueryList<StepComponent>();
106
+ component.steps.reset([]);
107
+ const originalIndex = component.activeStepIndex;
108
+
109
+ const emitSpy = vi.spyOn(component.maxReachedEvent as EventEmitter<null>, 'emit');
110
+
111
+ component.nextStep();
112
+
113
+ expect(component.activeStepIndex).toBe(originalIndex);
114
+ expect(emitSpy).not.toHaveBeenCalled();
115
+ });
116
+ });
117
+
118
+ describe("prevStep", () => {
119
+ it("deve voltar para o passo anterior quando não estiver no primeiro", () => {
120
+ const steps = [
121
+ { isActive: true } as StepComponent,
122
+ { isActive: true } as StepComponent,
123
+ { isActive: false } as StepComponent,
124
+ ];
125
+ component.steps = new QueryList<StepComponent>();
126
+ component.steps.reset(steps);
127
+ component.activeStepIndex = 1;
128
+
129
+ component.prevStep();
130
+
131
+ expect(component.activeStepIndex).toBe(0);
132
+ expect(steps[1].isActive).toBe(false);
133
+ expect(steps[0].isActive).toBe(true);
134
+ });
135
+
136
+ it("não deve fazer nada quando estiver no primeiro passo", () => {
137
+ const steps = [
138
+ { isActive: true } as StepComponent,
139
+ { isActive: false } as StepComponent
140
+ ];
141
+ component.steps = new QueryList<StepComponent>();
142
+ component.steps.reset(steps);
143
+ component.activeStepIndex = 0;
144
+
145
+ component.prevStep();
146
+
147
+ expect(component.activeStepIndex).toBe(0);
148
+ expect(steps[0].isActive).toBe(true);
149
+ });
150
+
151
+ it("não deve fazer nada quando não há passos", () => {
152
+ component.steps = new QueryList<StepComponent>();
153
+ component.steps.reset([]);
154
+ const originalIndex = component.activeStepIndex;
155
+
156
+ component.prevStep();
157
+
158
+ expect(component.activeStepIndex).toBe(originalIndex);
159
+ });
160
+ });
161
+
162
+ describe("resetStepper", () => {
163
+ it("deve redefinir para o primeiro passo e desativar todos os outros", () => {
164
+ const steps = [
165
+ { isActive: false } as StepComponent,
166
+ { isActive: true } as StepComponent,
167
+ { isActive: true } as StepComponent,
168
+ ];
169
+ component.steps = new QueryList<StepComponent>();
170
+ component.steps.reset(steps);
171
+ component.activeStepIndex = 2;
172
+
173
+ const forEachSpy = vi.spyOn(component.steps, "forEach");
174
+
175
+ component.resetStepper();
176
+
177
+ expect(component.activeStepIndex).toBe(0);
178
+ expect(forEachSpy).toHaveBeenCalled();
179
+ expect(steps[0].isActive).toBe(true);
180
+ expect(steps[1].isActive).toBe(false);
181
+ expect(steps[2].isActive).toBe(false);
182
+ });
183
+
184
+ it("não deve ativar nenhum passo quando não há passos", () => {
185
+ component.steps = new QueryList<StepComponent>();
186
+ component.steps.reset([]);
187
+ component.activeStepIndex = 2;
188
+
189
+ const forEachSpy = vi.spyOn(component.steps, "forEach");
190
+
191
+ component.resetStepper();
192
+
193
+ expect(component.activeStepIndex).toBe(0);
194
+ expect(forEachSpy).toHaveBeenCalled();
195
+ });
196
+ });
197
+
198
+ describe("Testes com Template", () => {
199
+ it("deve exibir o título do stepper quando fornecido", () => {
200
+ component.title = "Meu Stepper";
201
+ fixture.detectChanges();
202
+
203
+ const titleElement = fixture.debugElement.query(By.css(".stepper-title"));
204
+ expect(titleElement).toBeTruthy();
205
+ expect(titleElement.nativeElement.textContent).toContain("Meu Stepper");
206
+ });
207
+ });
208
+
209
+ // Testes adicionais para melhor cobertura
210
+ describe("Outros comportamentos", () => {
211
+ it("deve permitir navegação entre múltiplos passos", async() => {
212
+ const steps = [
213
+ { isActive: false } as StepComponent,
214
+ { isActive: false } as StepComponent,
215
+ { isActive: false } as StepComponent,
216
+ ];
217
+ component.steps = new QueryList<StepComponent>();
218
+ component.steps.reset(steps);
219
+
220
+ component.ngAfterContentInit();
221
+ expect(component.activeStepIndex).toBe(0);
222
+ expect(steps[0].isActive).toBe(true);
223
+
224
+ component.nextStep();
225
+
226
+ expect(component.activeStepIndex).toBe(1);
227
+ expect(component.steps.get(0)?.isActive).toBe(true);
228
+
229
+ component.nextStep();
230
+ expect(component.activeStepIndex).toBe(2);
231
+ expect(component.steps.get(2)?.isActive).toBe(true);
232
+
233
+ component.prevStep();
234
+ expect(component.activeStepIndex).toBe(1);
235
+ expect(component.steps.get(1)?.isActive).toBe(true);
236
+ });
237
+ });
238
+ });
@@ -0,0 +1,77 @@
1
+ import type { Meta, StoryObj } from '@storybook/angular-vite';
2
+ import { fn } from 'storybook/test';
3
+
4
+ import { StepperComponent } from './stepper';
5
+
6
+ const meta: Meta<StepperComponent> = {
7
+ title: 'Componentes/Stepper',
8
+ component: StepperComponent,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ title: { control: 'text' },
12
+ },
13
+ args: {
14
+ maxReachedEvent: fn(),
15
+ },
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<StepperComponent>;
20
+
21
+ const renderStepper = (steps: string) => ({
22
+ template: `
23
+ <cao-stepper title="Etapas do cadastro" (maxReachedEvent)="maxReachedEvent()">
24
+ ${steps}
25
+ </cao-stepper>
26
+ `,
27
+ props: { maxReachedEvent: fn() },
28
+ });
29
+
30
+ export const Padrao: Story = {
31
+ render: () =>
32
+ renderStepper(`
33
+ <cao-step title="Dados pessoais" />
34
+ <cao-step title="Endereço" />
35
+ <cao-step title="Confirmação" />
36
+ `),
37
+ };
38
+
39
+ export const ComTitulo: Story = {
40
+ render: () =>
41
+ renderStepper(`
42
+ <cao-step title="Etapa 1" />
43
+ <cao-step title="Etapa 2" />
44
+ <cao-step title="Etapa 3" />
45
+ `),
46
+ };
47
+
48
+ export const MultiplastEtapas: Story = {
49
+ render: () =>
50
+ renderStepper(`
51
+ <cao-step title="Cadastro" />
52
+ <cao-step title="Documentos" />
53
+ <cao-step title="Bancário" />
54
+ <cao-step title="Revisão" />
55
+ <cao-step title="Conclusão" />
56
+ `),
57
+ };
58
+
59
+ export const SomenteStep: Story = {
60
+ render: () => ({
61
+ template: `
62
+ <div style="display:flex;gap:16px">
63
+ <cao-step title="Ativa" isActive="true" />
64
+ <cao-step title="Inativa" />
65
+ </div>
66
+ `,
67
+ }),
68
+ };
69
+
70
+ export const StepComStepComponent: Story = {
71
+ render: () =>
72
+ renderStepper(`
73
+ <cao-step title="Primeira" />
74
+ <cao-step title="Segunda" />
75
+ <cao-step title="Terceira" isActive="true" />
76
+ `),
77
+ };
@@ -0,0 +1,57 @@
1
+ import { AfterContentInit, EventEmitter, Output , ContentChildren, Input, QueryList , Component } from "@angular/core";
2
+
3
+
4
+ import { StepComponent } from "./step/step";
5
+
6
+ @Component({
7
+ selector: "cao-stepper",
8
+ standalone: true,
9
+ templateUrl: "./stepper.html",
10
+ styleUrl: "./stepper.css",
11
+ })
12
+ export class StepperComponent implements AfterContentInit {
13
+ constructor() {}
14
+ @ContentChildren(StepComponent) steps!: QueryList<StepComponent>;
15
+ @Input() title!: string;
16
+ activeStepIndex = 0;
17
+ lengthStep = 0;
18
+ showLine = true;
19
+
20
+ @Output() readonly maxReachedEvent: EventEmitter<null> = new EventEmitter<null>();
21
+
22
+ ngAfterContentInit(): void {
23
+ if (this.steps.length > 0) {
24
+ this.steps.toArray()[0].isActive = true;
25
+ this.lengthStep = this.steps.length;
26
+ }
27
+ }
28
+
29
+ nextStep() {
30
+ if (this.steps.length > 0) {
31
+ if (this.activeStepIndex < this.steps.length - 1) {
32
+ this.activeStepIndex++;
33
+ this.steps.toArray()[this.activeStepIndex].isActive = true;
34
+ } else {
35
+ this.maxReachedEvent.emit(null);
36
+ }
37
+ }
38
+ }
39
+
40
+ prevStep() {
41
+ if (this.steps.length > 0) {
42
+ if (this.activeStepIndex > 0) {
43
+ this.steps.toArray()[this.activeStepIndex].isActive = false;
44
+ this.activeStepIndex--;
45
+ this.steps.toArray()[this.activeStepIndex].isActive = true;
46
+ }
47
+ }
48
+ }
49
+
50
+ resetStepper() {
51
+ this.activeStepIndex = 0;
52
+ this.steps.forEach((step) => (step.isActive = false));
53
+ if (this.steps.length > 0) {
54
+ this.steps.toArray()[0].isActive = true;
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,84 @@
1
+ <div
2
+ #header
3
+ [class]="'cao-tab-header cao-tab-header--align-' + labelAlign"
4
+ >
5
+ <div class="cao-tab-label-list" role="tablist">
6
+ @for (tab of tabList; track $index; let i = $index) {
7
+ <button
8
+ #tabBtn
9
+ role="tab"
10
+ type="button"
11
+ class="cao-tab-label"
12
+ [class.cao-tab-label--active]="isActive(i)"
13
+ [class.cao-tab-label--disabled]="tab.isDisabled"
14
+ [attr.aria-selected]="isActive(i)"
15
+ [attr.aria-disabled]="tab.isDisabled || null"
16
+ [attr.tabindex]="isActive(i) ? 0 : -1"
17
+ [attr.id]="'cao-tab-label-' + i"
18
+ [attr.aria-controls]="'cao-tab-content-' + i"
19
+ (click)="onTabClick(i)"
20
+ (keydown)="onKeydown($event, i)"
21
+ (focus)="onFocus(i)"
22
+ >
23
+ @if (tab.leadingIcon) {
24
+ <span class="leading-icon">
25
+ @if (tab.isLucideIcon) {
26
+ <svg [lucideIcon]="tab.leadingIcon" [style.color]="isActive(i) ? iconColorActive : iconColorInactive" [strokeWidth]="strokeWidthIcon"></svg>
27
+ } @else {
28
+ <img alt="icon" [src]="tab.leadingIcon" />
29
+ }
30
+ </span>
31
+ }
32
+
33
+ @if (tab.labelTemplate) {
34
+ <ng-container [ngTemplateOutlet]="tab.labelTemplate" />
35
+ } @else {
36
+ <span class="cao-tab-labeltext">{{ tab.label }}</span>
37
+ }
38
+
39
+ @if (tab.trailingIcon) {
40
+ <span class="trailing-icon">
41
+ @if (tab.isLucideIcon) {
42
+ <svg [lucideIcon]="tab.trailingIcon" [style.color]="isActive(i) ? iconColorActive : iconColorInactive" [strokeWidth]="strokeWidthIcon"></svg>
43
+ } @else {
44
+ <img alt="icon" [src]="tab.trailingIcon" />
45
+ }
46
+ </span>
47
+ }
48
+ </button>
49
+ }
50
+ </div>
51
+
52
+ <!-- Ink bar animado -->
53
+ <div class="cao-tab-ink-bar-track">
54
+ <div #inkBar class="cao-tab-ink-bar"></div>
55
+ </div>
56
+ </div>
57
+
58
+ <!-- ═══ Tab Bodies ════════════════════════════════════════════════════════ -->
59
+ <div
60
+ class="cao-tab-body-wrapper"
61
+ [class.cao-tab-body-wrapper--dynamic]="dynamicHeight"
62
+ >
63
+ @for (tab of tabList; track $index; let i = $index) {
64
+ <div
65
+ role="tabpanel"
66
+ class="cao-tab-body"
67
+ [class.cao-tab-body--active]="isActive(i)"
68
+ [attr.id]="'cao-tab-content-' + i"
69
+ [attr.aria-labelledby]="'cao-tab-label-' + i"
70
+ [attr.aria-hidden]="!isActive(i)"
71
+ [style.--cao-tab-anim-duration]="animationDuration"
72
+ >
73
+ <div class="cao-tab-bodycontent">
74
+ @if (tab.lazyContent) {
75
+ @if (isActive(i) || wasActivated(i)) {
76
+ <ng-container [ngTemplateOutlet]="tab.lazyContent" />
77
+ }
78
+ } @else {
79
+ <ng-container [ngTemplateOutlet]="tab.content" />
80
+ }
81
+ </div>
82
+ </div>
83
+ }
84
+ </div>
@@ -0,0 +1,151 @@
1
+ :host {
2
+ --cao-tab-font-family: inherit;
3
+ --cao-tab-font-size: 0.875rem;
4
+ --cao-tab-font-weight: 500;
5
+ --cao-tab-label-height: 48px;
6
+ --cao-tab-Label-px: 16px;
7
+ --cao-tab-Label-gap: 6px;
8
+
9
+ --cao-tab-color-text: #6b7280;
10
+ --cao-tab-color-active: #4f46e5;
11
+ --cao-tab-color-hover-bg: #f3f4f6;
12
+ --cao-tab-color-disabled: #d1d5db;
13
+ --cao-tab-color-ink-bar: #4f46e5;
14
+ --cao-tab-color-header-border: #e5e7eb;
15
+
16
+ --cao-tab-color-header-border: #e5e7eb;
17
+
18
+ --cao-tab-ink-bar-height: 2px;
19
+ --cao-tab-content-pt: 20px;
20
+ --cao-tab-radius: 4px;
21
+
22
+ display: flex;
23
+ flex-direction: column;
24
+ width: 100%;
25
+ }
26
+
27
+ .cao-tab-header {
28
+ position: relative;
29
+ border-bottom: 1px solid var(--cao-tab-color-header-border);
30
+ overflow-x: auto;
31
+ scrollbar-width: none;
32
+ }
33
+ .cao-tab-header::-webkit-scrollbar { display: none; }
34
+
35
+ .cao-tab-label-list {
36
+ display: flex;
37
+ align-items: center;
38
+ min-width: max-content;
39
+ }
40
+
41
+ .cao-tab-header--align-center .cao-tab-label-list { justify-content: center; }
42
+ .cao-tab-header--align-end .cao-tab-label-list { justify-content: flex-end; }
43
+ .cao-tab-header--align-start .cao-tab-label-list { justify-content: flex-start; }
44
+
45
+ .cao-tab-label {
46
+ width: 100%;
47
+ display: inline-flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ align-items: center;
51
+ gap: var(--cao-tab-label-gap);
52
+ height: var(--cao-tab-label-height);
53
+ padding: 0 var(--cao-tab-label-px);
54
+
55
+ font-family: var(--cao-tab-font-family);
56
+ font-size: var(--cao-tab-font-size);
57
+ font-weight: var(--cao-tab-font-weight);
58
+ color: var(--cao-tab-color-text);
59
+ white-space: nowrap;
60
+
61
+ background: transparent;
62
+ border: none;
63
+ border-radius: var(--cao-tab-radius) var(--cao-tab-radius) 0 0;
64
+ cursor: pointer;
65
+ user-select: none;
66
+ transition: color 180ms ease, background-color 180ms ease;
67
+
68
+ outline: none;
69
+ }
70
+
71
+ .cao-tab-label:focus-visible {
72
+ outline: 2px solid var(--cao-tab-color-active);
73
+ outline-offset: -2px;
74
+ }
75
+
76
+ .cao-tab-label:hover:not(.cao-tab-label--disabled) {
77
+ background-color: var(--cao-tab-color-hover-bg);
78
+ color: var(--cao-tab-color-active);
79
+ }
80
+
81
+ .cao-tab-label--active {
82
+ color: var(--cao-tab-color-active);
83
+ }
84
+
85
+ .cao-tab-label--disabled {
86
+ color: var(--cao-tab-color-disabled);
87
+ cursor: not-allowed;
88
+ pointer-events: none;
89
+ }
90
+
91
+ .cao-tab-label__icon {
92
+ flex-shrink: 0;
93
+ color: inherit;
94
+ transition: color 180ms ease;
95
+ }
96
+
97
+ .cao-tab-ink-bar-track {
98
+ position: absolute;
99
+ bottom: 0;
100
+ left: 0;
101
+ right: 0;
102
+ height: var(--cao-tab-ink-bar-height);
103
+ pointer-events: none;
104
+ }
105
+
106
+ .cao-tab-ink-bar {
107
+ position: absolute;
108
+ bottom: 0;
109
+ left: 0;
110
+ height: 100%;
111
+ background-color: var(--cao-tab-color-ink-bar);
112
+ border-radius: 2px 2px 0 0;
113
+ transition-property: transform, width;
114
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
115
+ transition-duration: 300ms; /* sobrescrito por JS */
116
+ }
117
+
118
+ .cao-tab-body-wrapper {
119
+ position: relative;
120
+ overflow: hidden;
121
+ }
122
+
123
+ .cao-tab-body-wrapper--dynamic {
124
+ height: auto;
125
+ }
126
+
127
+ .cao-tab-body {
128
+ position: absolute;
129
+ top: 0;
130
+ left: 0;
131
+ width: 100%;
132
+ visibility: hidden;
133
+ opacity: 0;
134
+ pointer-events: none;
135
+ transform: translateX(24px);
136
+ transition:
137
+ opacity var(--cao-tab-anim-duration, 300ms) cubic-bezier(0.4, 0, 0.2, 1),
138
+ transform var(--cao-tab-anim-duration, 300ms) cubic-bezier(0.4, 0, 0.2, 1);
139
+ }
140
+
141
+ .cao-tab-body--active {
142
+ position: relative;
143
+ visibility: visible;
144
+ opacity: 1;
145
+ pointer-events: auto;
146
+ transform: translateX(0);
147
+ }
148
+
149
+ .cao-tab-body__content {
150
+ padding-top: var(--cao-tab-content-pt);
151
+ }
@@ -0,0 +1,22 @@
1
+ import { ComponentFixture, TestBed } from "@angular/core/testing";
2
+
3
+ import { CaoTabGroup } from "./tab-group";
4
+
5
+ describe("TabGroup", () => {
6
+ let component: CaoTabGroup;
7
+ let fixture: ComponentFixture<CaoTabGroup>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [CaoTabGroup],
12
+ }).compileComponents();
13
+
14
+ fixture = TestBed.createComponent(CaoTabGroup);
15
+ component = fixture.componentInstance;
16
+ await fixture.whenStable();
17
+ });
18
+
19
+ it("should create", () => {
20
+ expect(component).toBeTruthy();
21
+ });
22
+ });