ngx-opalbytes-components 1.18.0 → 1.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/README.md +103 -0
- package/cli/cao-comp.cjs +186 -0
- package/fesm2022/ngx-opalbytes-components.mjs +331 -76
- package/fesm2022/ngx-opalbytes-components.mjs.map +1 -1
- package/package.json +4 -1
- package/src/lib/shared/components/autocomplete/autocomplete.component.html +126 -0
- package/src/lib/shared/components/autocomplete/autocomplete.component.scss +150 -0
- package/src/lib/shared/components/autocomplete/autocomplete.component.ts +552 -0
- package/src/lib/shared/components/base-button/base-button.component.html +33 -0
- package/src/lib/shared/components/base-button/base-button.component.scss +14 -0
- package/src/lib/shared/components/base-button/base-button.component.spec.ts +64 -0
- package/src/lib/shared/components/base-button/base-button.component.ts +48 -0
- package/src/lib/shared/components/base-icon/base-icon.css +0 -0
- package/src/lib/shared/components/base-icon/base-icon.html +11 -0
- package/src/lib/shared/components/base-icon/base-icon.spec.ts +23 -0
- package/src/lib/shared/components/base-icon/base-icon.ts +18 -0
- package/src/lib/shared/components/base-input/base-input.css +193 -0
- package/src/lib/shared/components/base-input/base-input.html +43 -0
- package/src/lib/shared/components/base-input/base-input.spec.ts +146 -0
- package/src/lib/shared/components/base-input/base-input.ts +133 -0
- package/src/lib/shared/components/base-time-range/base-time-range.css +27 -0
- package/src/lib/shared/components/base-time-range/base-time-range.html +23 -0
- package/src/lib/shared/components/base-time-range/base-time-range.spec.ts +27 -0
- package/src/lib/shared/components/base-time-range/base-time-range.ts +61 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.html +28 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.scss +45 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.spec.ts +22 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.ts +138 -0
- package/src/lib/shared/components/card/card.component.css +264 -0
- package/src/lib/shared/components/card/card.component.html +103 -0
- package/src/lib/shared/components/card/card.component.ts +63 -0
- package/src/lib/shared/components/date-picker/date-picker.html +82 -0
- package/src/lib/shared/components/date-picker/date-picker.scss +8 -0
- package/src/lib/shared/components/date-picker/date-picker.spec.ts +23 -0
- package/src/lib/shared/components/date-picker/date-picker.ts +165 -0
- package/src/lib/shared/components/drop-down/drop-down.css +61 -0
- package/src/lib/shared/components/drop-down/drop-down.html +96 -0
- package/src/lib/shared/components/drop-down/drop-down.spec.ts +335 -0
- package/src/lib/shared/components/drop-down/drop-down.ts +335 -0
- package/src/lib/shared/components/footer/footer.css +29 -0
- package/src/lib/shared/components/footer/footer.html +10 -0
- package/src/lib/shared/components/footer/footer.spec.ts +22 -0
- package/src/lib/shared/components/footer/footer.ts +15 -0
- package/src/lib/shared/components/index.ts +19 -0
- package/src/lib/shared/components/kpi/kpi.css +0 -0
- package/src/lib/shared/components/kpi/kpi.html +11 -0
- package/src/lib/shared/components/kpi/kpi.spec.ts +38 -0
- package/src/lib/shared/components/kpi/kpi.ts +25 -0
- package/src/lib/shared/components/links-button/links-button.css +70 -0
- package/src/lib/shared/components/links-button/links-button.html +23 -0
- package/src/lib/shared/components/links-button/links-button.spec.ts +139 -0
- package/src/lib/shared/components/links-button/links-button.ts +45 -0
- package/src/lib/shared/components/paginator/paginator.css +0 -0
- package/src/lib/shared/components/paginator/paginator.html +10 -0
- package/src/lib/shared/components/paginator/paginator.spec.ts +23 -0
- package/src/lib/shared/components/paginator/paginator.ts +25 -0
- package/src/lib/shared/components/stepper/step/step.css +8 -0
- package/src/lib/shared/components/stepper/step/step.html +1 -0
- package/src/lib/shared/components/stepper/step/step.spec.ts +131 -0
- package/src/lib/shared/components/stepper/step/step.ts +14 -0
- package/src/lib/shared/components/stepper/stepper.css +17 -0
- package/src/lib/shared/components/stepper/stepper.html +11 -0
- package/src/lib/shared/components/stepper/stepper.spec.ts +238 -0
- package/src/lib/shared/components/stepper/stepper.ts +57 -0
- package/src/lib/shared/components/tab-group/tab-group.component.html +84 -0
- package/src/lib/shared/components/tab-group/tab-group.component.scss +151 -0
- package/src/lib/shared/components/tab-group/tab-group.spec.ts +22 -0
- package/src/lib/shared/components/tab-group/tab-group.ts +218 -0
- package/src/lib/shared/components/tab-panel/tab-content.directive.ts +7 -0
- package/src/lib/shared/components/tab-panel/tab-label.directive.ts +7 -0
- package/src/lib/shared/components/tab-panel/tab-panel.css +0 -0
- package/src/lib/shared/components/tab-panel/tab-panel.spec.ts +22 -0
- package/src/lib/shared/components/tab-panel/tab-panel.ts +31 -0
- package/src/lib/shared/components/time-picker/time-picker.css +84 -0
- package/src/lib/shared/components/time-picker/time-picker.html +21 -0
- package/src/lib/shared/components/time-picker/time-picker.spec.ts +23 -0
- package/src/lib/shared/components/time-picker/time-picker.ts +90 -0
- package/types/ngx-opalbytes-components.d.ts +70 -13
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import {
|
|
3
|
+
Component,
|
|
4
|
+
ContentChildren,
|
|
5
|
+
QueryList,
|
|
6
|
+
AfterContentInit,
|
|
7
|
+
OnDestroy,
|
|
8
|
+
Input,
|
|
9
|
+
Output,
|
|
10
|
+
EventEmitter,
|
|
11
|
+
signal,
|
|
12
|
+
WritableSignal,
|
|
13
|
+
inject,
|
|
14
|
+
PLATFORM_ID,
|
|
15
|
+
ElementRef,
|
|
16
|
+
ViewChildren,
|
|
17
|
+
ViewChild,
|
|
18
|
+
HostBinding,
|
|
19
|
+
} from '@angular/core';
|
|
20
|
+
|
|
21
|
+
import { LucideDynamicIcon } from '@lucide/angular';
|
|
22
|
+
import { Subscription } from 'rxjs';
|
|
23
|
+
|
|
24
|
+
import { CaoTabPanel } from '../tab-panel/tab-panel';
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export interface ICaoTabChangeEvent {
|
|
28
|
+
previousIndex: number;
|
|
29
|
+
index: number;
|
|
30
|
+
tab: CaoTabPanel;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Component({
|
|
34
|
+
selector: 'cao-tab-group',
|
|
35
|
+
standalone: true,
|
|
36
|
+
imports: [NgTemplateOutlet, LucideDynamicIcon],
|
|
37
|
+
templateUrl: './tab-group.component.html',
|
|
38
|
+
styleUrl: './tab-group.component.scss',
|
|
39
|
+
host: { class: 'cao-tab-group' },
|
|
40
|
+
})
|
|
41
|
+
export class CaoTabGroup implements AfterContentInit, OnDestroy {
|
|
42
|
+
@HostBinding('style')
|
|
43
|
+
get _hostStyles(): Record<string, string> {
|
|
44
|
+
return {
|
|
45
|
+
'--cao-tab-label-height': this.labelHeight,
|
|
46
|
+
'--cao-tab-label-px': this.labelPaddingX,
|
|
47
|
+
'--cao-tab-label-gap': this.labelGap,
|
|
48
|
+
'--cao-tab-color-text': this.colorText,
|
|
49
|
+
'--cao-tab-color-active': this.colorActive,
|
|
50
|
+
'--cao-tab-color-hover-bg': this.colorHoverBg,
|
|
51
|
+
'--cao-tab-color-disabled': this.colorDisabled,
|
|
52
|
+
'--cao-tab-color-ink-bar': this.colorInkBar,
|
|
53
|
+
'--cao-tab-color-header-border': this.colorHeaderBorder,
|
|
54
|
+
'--cao-tab-ink-bar-height': this.inkBarHeight,
|
|
55
|
+
'--cao-tab-content-pt': this.contentPaddingTop,
|
|
56
|
+
'--cao-tab-radius': this.labelRadius,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
@Input()
|
|
60
|
+
set selectedIndex(val: number) {
|
|
61
|
+
this.selectIndex(val, false);
|
|
62
|
+
}
|
|
63
|
+
get selectedIndex(): number {
|
|
64
|
+
return this.activeIndex();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Input() labelHeight = '48px';
|
|
68
|
+
@Input() labelPaddingX = '16px';
|
|
69
|
+
@Input() labelGap = '6px';
|
|
70
|
+
@Input() colorText = '#6b7280';
|
|
71
|
+
@Input() colorActive = '#4f46e5';
|
|
72
|
+
@Input() colorHoverBg = '#f3f4f6';
|
|
73
|
+
@Input() colorDisabled = '#d1d5db';
|
|
74
|
+
@Input() colorInkBar = '#4f46e5';
|
|
75
|
+
@Input() colorHeaderBorder = '#e5e7eb';
|
|
76
|
+
@Input() inkBarHeight = '2px';
|
|
77
|
+
@Input() contentPaddingTop = '20px';
|
|
78
|
+
@Input() labelRadius = '4px';
|
|
79
|
+
@Input() animationDuration = '300ms';
|
|
80
|
+
@Input() dynamicHeight = false;
|
|
81
|
+
|
|
82
|
+
@Input() labelAlign: 'start' | 'center' | 'end' = 'start';
|
|
83
|
+
@Input() iconColorActive = ''
|
|
84
|
+
@Input() iconColorInactive = ''
|
|
85
|
+
@Input() strokeWidthIcon = 1
|
|
86
|
+
|
|
87
|
+
@Output() readonly selectedTabChange = new EventEmitter<ICaoTabChangeEvent>();
|
|
88
|
+
|
|
89
|
+
@Output() readonly focusChange = new EventEmitter<number>();
|
|
90
|
+
|
|
91
|
+
@ContentChildren(CaoTabPanel) tabs!: QueryList<CaoTabPanel>;
|
|
92
|
+
@ViewChildren('tabBtn') tabBtns!: QueryList<ElementRef<HTMLButtonElement>>;
|
|
93
|
+
@ViewChild('inkBar') inkBar!: ElementRef<HTMLElement>;
|
|
94
|
+
|
|
95
|
+
readonly activeIndex: WritableSignal<number> = signal(0);
|
|
96
|
+
|
|
97
|
+
private activatedSet = new Set<number>();
|
|
98
|
+
|
|
99
|
+
tabList: CaoTabPanel[] = [];
|
|
100
|
+
|
|
101
|
+
private subs = new Subscription();
|
|
102
|
+
private isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
103
|
+
|
|
104
|
+
ngAfterContentInit(): void {
|
|
105
|
+
this.tabList = this.tabs.toArray();
|
|
106
|
+
this.activatedSet.add(this.activeIndex());
|
|
107
|
+
|
|
108
|
+
this.subs.add(
|
|
109
|
+
this.tabs.changes.subscribe(() => {
|
|
110
|
+
this.tabList = this.tabs.toArray();
|
|
111
|
+
const safe = Math.min(this.activeIndex(), this.tabList.length - 1);
|
|
112
|
+
this.activeIndex.set(Math.max(0, safe));
|
|
113
|
+
this.scheduleInkBar();
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
this.scheduleInkBar();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ngOnDestroy(): void {
|
|
121
|
+
this.subs.unsubscribe();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
selectTab(index: number): void {
|
|
125
|
+
this.selectIndex(index, true);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get activeTab(): CaoTabPanel | null {
|
|
129
|
+
return this.tabList[this.activeIndex()] ?? null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
isActive(index: number): boolean {
|
|
133
|
+
return this.activeIndex() === index;
|
|
134
|
+
}
|
|
135
|
+
wasActivated(index: number): boolean {
|
|
136
|
+
return this.activatedSet.has(index);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
onTabClick(index: number): void {
|
|
140
|
+
if (this.tabList[index]?.isDisabled) {return;}
|
|
141
|
+
this.selectIndex(index, true);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
onKeydown(event: KeyboardEvent, currentIndex: number): void {
|
|
145
|
+
let next = currentIndex;
|
|
146
|
+
|
|
147
|
+
switch (event.key) {
|
|
148
|
+
case 'ArrowRight': next = this.nextEnabled(currentIndex, 1); break;
|
|
149
|
+
case 'ArrowLeft': next = this.nextEnabled(currentIndex, -1); break;
|
|
150
|
+
case 'Home': next = this.nextEnabled(-1, 1); break;
|
|
151
|
+
case 'End': next = this.nextEnabled(this.tabList.length, -1); break;
|
|
152
|
+
default: return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
this.selectIndex(next, true);
|
|
157
|
+
this.focusTab(next);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
onFocus(index: number): void {
|
|
161
|
+
this.focusChange.emit(index);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private nextEnabled(from: number, dir: 1 | -1): number {
|
|
165
|
+
const tabs = this.tabList;
|
|
166
|
+
let idx = from + dir;
|
|
167
|
+
while (idx >= 0 && idx < tabs.length) {
|
|
168
|
+
if (!tabs[idx].isDisabled) {return idx;}
|
|
169
|
+
idx += dir;
|
|
170
|
+
}
|
|
171
|
+
return from;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private selectIndex(index: number, emit: boolean): void {
|
|
175
|
+
if (!this.tabList) {
|
|
176
|
+
this.activeIndex.set(Math.max(0, index));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const clamped = Math.max(0, Math.min(index, this.tabList.length - 1));
|
|
180
|
+
const previous = this.activeIndex();
|
|
181
|
+
if (clamped === previous && emit) {return;}
|
|
182
|
+
|
|
183
|
+
this.activeIndex.set(clamped);
|
|
184
|
+
this.activatedSet.add(clamped);
|
|
185
|
+
|
|
186
|
+
if (emit) {
|
|
187
|
+
this.selectedTabChange.emit({
|
|
188
|
+
previousIndex: previous,
|
|
189
|
+
index: clamped,
|
|
190
|
+
tab: this.tabList[clamped],
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.scheduleInkBar();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private focusTab(index: number): void {
|
|
198
|
+
this.tabBtns?.toArray()[index]?.nativeElement?.focus();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
scheduleInkBar(): void {
|
|
202
|
+
if (!this.isBrowser) {return;}
|
|
203
|
+
requestAnimationFrame(() => this.updateInkBar());
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private updateInkBar(): void {
|
|
207
|
+
const btns = this.tabBtns?.toArray();
|
|
208
|
+
const bar = this.inkBar?.nativeElement;
|
|
209
|
+
if (!btns || !bar) {return;}
|
|
210
|
+
|
|
211
|
+
const btn = btns[this.activeIndex()]?.nativeElement;
|
|
212
|
+
if (!btn) {return;}
|
|
213
|
+
|
|
214
|
+
bar.style.width = `${btn.offsetWidth}px`;
|
|
215
|
+
bar.style.transform = `translateX(${btn.offsetLeft}px)`;
|
|
216
|
+
bar.style.transitionDuration = this.animationDuration;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
2
|
+
|
|
3
|
+
import { CaoTabPanel } from "./tab-panel";
|
|
4
|
+
|
|
5
|
+
describe("CaoTabPanel", () => {
|
|
6
|
+
let component: CaoTabPanel;
|
|
7
|
+
let fixture: ComponentFixture<CaoTabPanel>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [CaoTabPanel],
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
|
|
14
|
+
fixture = TestBed.createComponent(CaoTabPanel);
|
|
15
|
+
component = fixture.componentInstance;
|
|
16
|
+
await fixture.whenStable();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should create", () => {
|
|
20
|
+
expect(component).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Component, ContentChild, Input, TemplateRef, ViewChild } from "@angular/core"
|
|
2
|
+
|
|
3
|
+
import { CaoTabContentDirective } from "./tab-content.directive"
|
|
4
|
+
import { CaoTabLabelDirective } from "./tab-label.directive"
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: "cao-tab-panel",
|
|
8
|
+
imports: [],
|
|
9
|
+
template: `
|
|
10
|
+
<ng-template #implicitContent>
|
|
11
|
+
<ng-content />
|
|
12
|
+
</ng-template>
|
|
13
|
+
`,
|
|
14
|
+
styleUrl: "./tab-panel.css",
|
|
15
|
+
})
|
|
16
|
+
export class CaoTabPanel {
|
|
17
|
+
@Input() label = ''
|
|
18
|
+
@Input() isLucideIcon = true
|
|
19
|
+
@Input() trailingIcon: string | null = null
|
|
20
|
+
@Input() leadingIcon: string | null = null
|
|
21
|
+
@Input() isDisabled = false
|
|
22
|
+
|
|
23
|
+
@ContentChild(CaoTabLabelDirective, { read: TemplateRef })
|
|
24
|
+
labelTemplate?: TemplateRef<unknown>;
|
|
25
|
+
|
|
26
|
+
@ContentChild(CaoTabContentDirective, { read: TemplateRef })
|
|
27
|
+
lazyContent?: TemplateRef<unknown>;
|
|
28
|
+
|
|
29
|
+
@ViewChild('implicitContent', { static: true })
|
|
30
|
+
content!: TemplateRef<unknown>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.time-picker-wrapper {
|
|
2
|
+
width: 130px;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
position: relative;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.custom-timepicker {
|
|
9
|
+
width: 100%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.custom-timepicker .mat-mdc-text-field-wrapper {
|
|
13
|
+
border: 1px solid transparent;
|
|
14
|
+
border-radius: 8px;
|
|
15
|
+
padding: 0;
|
|
16
|
+
height: 43px;
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
background-color: var(--input-background);
|
|
20
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
21
|
+
transition: border-color 0.2s ease-in-out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.custom-timepicker.invalid .mat-mdc-text-field-wrapper {
|
|
25
|
+
border-color: var(--error);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* só aplica o verde de success enquanto estiver focado */
|
|
29
|
+
.custom-timepicker.valid.mat-focused .mat-mdc-text-field-wrapper {
|
|
30
|
+
border-color: var(--success);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* após perder o foco, "neutraliza" o success para transparente */
|
|
34
|
+
.custom-timepicker.valid:not(.mat-focused) .mat-mdc-text-field-wrapper {
|
|
35
|
+
border-color: transparent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.custom-timepicker .mdc-line-ripple {
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.custom-timepicker .mat-mdc-input-element {
|
|
43
|
+
font-size: 16px;
|
|
44
|
+
padding-left: 16px;
|
|
45
|
+
caret-color: transparent;
|
|
46
|
+
background: transparent;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.custom-timepicker .mat-mdc-icon-button.mat-mdc-button-base {
|
|
50
|
+
padding: 0;
|
|
51
|
+
border-left: 1px solid var(--line-color);
|
|
52
|
+
border-radius: 0;
|
|
53
|
+
margin: 0;
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.custom-timepicker .mat-timepicker-toggle-default-icon {
|
|
60
|
+
color: var(--primary);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.custom-timepicker .mat-mdc-form-field-flex {
|
|
64
|
+
background-color: var(--input-background);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* oculta label flutuante e subscript conforme seu estilo */
|
|
68
|
+
.mat-mdc-form-field:not(.mat-form-field-disabled) .mat-mdc-floating-label.mdc-floating-label {
|
|
69
|
+
display: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.mat-mdc-form-field-subscript-wrapper,
|
|
73
|
+
.mat-mdc-form-field-bottom-align::before {
|
|
74
|
+
display: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.custom-timepicker .mat-mdc-form-field-infix {
|
|
78
|
+
padding: 18px 0 !important;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* remove qualquer box-shadow remanescente no outline quando focado */
|
|
82
|
+
.custom-timepicker.mat-form-field-appearance-outline.mat-focused .mat-form-field-outline-thick {
|
|
83
|
+
box-shadow: none !important;
|
|
84
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<div class="time-picker-wrapper">
|
|
2
|
+
<mat-form-field
|
|
3
|
+
class="custom-timepicker"
|
|
4
|
+
[ngClass]="{
|
|
5
|
+
'invalid': control?.touched && control?.invalid,
|
|
6
|
+
'valid': control?.touched && control?.valid
|
|
7
|
+
}">
|
|
8
|
+
<input
|
|
9
|
+
matInput
|
|
10
|
+
placeholder="00:00"
|
|
11
|
+
[formControl]="control"
|
|
12
|
+
[matTimepicker]="picker"
|
|
13
|
+
[min]="min"
|
|
14
|
+
[max]="max"
|
|
15
|
+
[disabled]="isDisabled"
|
|
16
|
+
[attr.data-cy]="dataCy"
|
|
17
|
+
(blur)="control?.markAsTouched()" />
|
|
18
|
+
<mat-timepicker-toggle matSuffix [for]="picker" />
|
|
19
|
+
<mat-timepicker #picker [options]="customOptions" (selected)="timeChange.emit($event.value)" />
|
|
20
|
+
</mat-form-field>
|
|
21
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { TimePicker } from './time-picker';
|
|
4
|
+
|
|
5
|
+
describe('TimePicker', () => {
|
|
6
|
+
let component: TimePicker;
|
|
7
|
+
let fixture: ComponentFixture<TimePicker>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [TimePicker]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(TimePicker);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
await fixture.whenStable();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
|
3
|
+
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
4
|
+
import { DateAdapter } from '@angular/material/core';
|
|
5
|
+
import { MatTimepickerOption } from '@angular/material/timepicker';
|
|
6
|
+
|
|
7
|
+
import { MaterialModule } from '../../../material.module';
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'cao-time-picker',
|
|
11
|
+
imports: [ MaterialModule, CommonModule, ReactiveFormsModule, ],
|
|
12
|
+
templateUrl: './time-picker.html',
|
|
13
|
+
styleUrl: './time-picker.css',
|
|
14
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
15
|
+
encapsulation: ViewEncapsulation.None,
|
|
16
|
+
})
|
|
17
|
+
export class TimePicker {
|
|
18
|
+
@Input() control: FormControl<Date | null> = new FormControl<Date | null>(null);
|
|
19
|
+
@Input() isDisabled = false;
|
|
20
|
+
@Input() dataCy?: string;
|
|
21
|
+
@Input() set min(v: string) {
|
|
22
|
+
this._min = v;
|
|
23
|
+
this.minInNumber = this.convertStringInNumber(v)
|
|
24
|
+
this.customOptions = this.generateTimeOptions().filter((option) => option.label >= this._min);
|
|
25
|
+
}
|
|
26
|
+
@Input() set max(v: string) {
|
|
27
|
+
this._max = v;
|
|
28
|
+
this.maxInNumber = this.convertStringInNumber(v)
|
|
29
|
+
this.customOptions = this.generateTimeOptions().filter((option) => option.label <= this._max);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
minInNumber = 7;
|
|
33
|
+
maxInNumber = 23;
|
|
34
|
+
|
|
35
|
+
@Output() readonly timeChange = new EventEmitter<Date>();
|
|
36
|
+
|
|
37
|
+
private _min = "07:00";
|
|
38
|
+
private _max = "23:00";
|
|
39
|
+
|
|
40
|
+
customOptions: MatTimepickerOption<Date>[] = this.generateTimeOptions();
|
|
41
|
+
|
|
42
|
+
constructor(private _adapter: DateAdapter<any>) {
|
|
43
|
+
this._adapter.setLocale("pt-BR");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get errorText(): string | null {
|
|
47
|
+
if (!this.control) {return null;}
|
|
48
|
+
|
|
49
|
+
if (this.control.hasError("required") || (this.control.touched && !this.control.value)) {
|
|
50
|
+
return "*Campo obrigatório";
|
|
51
|
+
}
|
|
52
|
+
if (this.control.hasError("quarterHour") && this.control.touched) {
|
|
53
|
+
return "Formato inválido";
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private convertStringInNumber(hour: string): number{
|
|
59
|
+
const explodedHour = parseInt(hour.split(":")[0])
|
|
60
|
+
if(typeof explodedHour !== 'number'){
|
|
61
|
+
return explodedHour
|
|
62
|
+
}
|
|
63
|
+
return explodedHour
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private generateTimeOptions(): MatTimepickerOption<Date>[] {
|
|
67
|
+
const options: MatTimepickerOption<Date>[] = [];
|
|
68
|
+
const baseDate = new Date(2025, 0, 1);
|
|
69
|
+
|
|
70
|
+
for (let hour = this.minInNumber; hour <= this.maxInNumber; hour++) {
|
|
71
|
+
for (let minute = 0; minute < 60; minute += 15) {
|
|
72
|
+
if (hour === this.maxInNumber && minute > 0) {continue;}
|
|
73
|
+
const date = new Date(baseDate);
|
|
74
|
+
date.setHours(hour, minute, 0);
|
|
75
|
+
const label = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
|
|
76
|
+
options.push({ label, value: date });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
updateTimeOptions(options: MatTimepickerOption<Date>[]): void {
|
|
84
|
+
this.customOptions = options;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setDisabledState(isDisabled: boolean): void {
|
|
88
|
+
this.isDisabled = isDisabled;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { OnInit, EventEmitter, OnChanges,
|
|
2
|
+
import { OnInit, EventEmitter, OnChanges, OnDestroy, AfterViewInit, ElementRef, TemplateRef, SimpleChanges, ChangeDetectorRef, Signal, AfterContentInit, QueryList, WritableSignal } from '@angular/core';
|
|
3
3
|
import { ControlValueAccessor, AbstractControl, FormControl } from '@angular/forms';
|
|
4
4
|
import { MatDateRangeInput } from '@angular/material/datepicker';
|
|
5
|
-
import { MatAutocompleteTrigger, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
|
6
5
|
import * as rxjs from 'rxjs';
|
|
7
6
|
import { Observable } from 'rxjs';
|
|
8
7
|
import { Router, ActivatedRoute } from '@angular/router';
|
|
@@ -30,10 +29,11 @@ declare class CaoBaseButtonComponent implements OnInit {
|
|
|
30
29
|
iconColor?: string;
|
|
31
30
|
strokeWidthIcon: number;
|
|
32
31
|
minTextToTooltip: number;
|
|
32
|
+
iconSize: number;
|
|
33
33
|
ngOnInit(): void;
|
|
34
34
|
onClick(): void;
|
|
35
35
|
static ɵfac: i0.ɵɵFactoryDeclaration<CaoBaseButtonComponent, never>;
|
|
36
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<CaoBaseButtonComponent, "cao-button", never, { "buttonText": { "alias": "buttonText"; "required": false; }; "isDarkMode": { "alias": "isDarkMode"; "required": false; }; "isDisabled": { "alias": "isDisabled"; "required": false; }; "isLoading": { "alias": "isLoading"; "required": false; }; "btnClass": { "alias": "btnClass"; "required": false; }; "tooltip": { "alias": "tooltip"; "required": false; }; "trailingIcon": { "alias": "trailingIcon"; "required": false; }; "leadingIcon": { "alias": "leadingIcon"; "required": false; }; "isLucideIcon": { "alias": "isLucideIcon"; "required": false; }; "dataCy": { "alias": "dataCy"; "required": false; }; "iconColor": { "alias": "iconColor"; "required": false; }; "strokeWidthIcon": { "alias": "strokeWidthIcon"; "required": false; }; "minTextToTooltip": { "alias": "minTextToTooltip"; "required": false; }; }, { "buttonClick": "buttonClick"; }, never, never, true, never>;
|
|
36
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CaoBaseButtonComponent, "cao-button", never, { "buttonText": { "alias": "buttonText"; "required": false; }; "isDarkMode": { "alias": "isDarkMode"; "required": false; }; "isDisabled": { "alias": "isDisabled"; "required": false; }; "isLoading": { "alias": "isLoading"; "required": false; }; "btnClass": { "alias": "btnClass"; "required": false; }; "tooltip": { "alias": "tooltip"; "required": false; }; "trailingIcon": { "alias": "trailingIcon"; "required": false; }; "leadingIcon": { "alias": "leadingIcon"; "required": false; }; "isLucideIcon": { "alias": "isLucideIcon"; "required": false; }; "dataCy": { "alias": "dataCy"; "required": false; }; "iconColor": { "alias": "iconColor"; "required": false; }; "strokeWidthIcon": { "alias": "strokeWidthIcon"; "required": false; }; "minTextToTooltip": { "alias": "minTextToTooltip"; "required": false; }; "iconSize": { "alias": "iconSize"; "required": false; }; }, { "buttonClick": "buttonClick"; }, never, never, true, never>;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
type CaoTypeInputs = 'text' | 'textarea' | 'number' | 'password' | 'date';
|
|
@@ -110,7 +110,7 @@ interface ICaoAutoCompleteOption {
|
|
|
110
110
|
icon?: string;
|
|
111
111
|
iconPosition?: string;
|
|
112
112
|
}
|
|
113
|
-
declare class CaoAutocompleteComponent implements OnInit, OnChanges, ControlValueAccessor {
|
|
113
|
+
declare class CaoAutocompleteComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit, ControlValueAccessor {
|
|
114
114
|
label: string;
|
|
115
115
|
placeholder: string;
|
|
116
116
|
options: ICaoAutoCompleteOption[];
|
|
@@ -127,43 +127,83 @@ declare class CaoAutocompleteComponent implements OnInit, OnChanges, ControlValu
|
|
|
127
127
|
autocompleteBgColor: string;
|
|
128
128
|
strokeWidthIcon: number;
|
|
129
129
|
isDisabled: boolean;
|
|
130
|
+
inputHeigth: string;
|
|
131
|
+
iconSize: number;
|
|
132
|
+
checkIcon: string;
|
|
133
|
+
showCheckIcon: boolean;
|
|
134
|
+
isLucideCheckIcon: boolean;
|
|
130
135
|
readonly itemSelected: EventEmitter<ICaoAutoCompleteOption>;
|
|
131
136
|
readonly blurEvent: EventEmitter<void>;
|
|
132
137
|
readonly focusEvent: EventEmitter<void>;
|
|
133
138
|
readonly leadingIconClick: EventEmitter<Event | MouseEvent>;
|
|
134
139
|
readonly trailingIconClick: EventEmitter<Event | MouseEvent>;
|
|
135
|
-
|
|
140
|
+
inputEl: ElementRef<HTMLInputElement>;
|
|
141
|
+
wrapperEl: ElementRef<HTMLDivElement>;
|
|
142
|
+
panelTemplate: TemplateRef<any>;
|
|
143
|
+
/** controla apenas o texto digitado / exibido no input */
|
|
136
144
|
internalControl: FormControl<string | null>;
|
|
137
145
|
filteredOptions$: Observable<ICaoAutoCompleteOption[]>;
|
|
146
|
+
filteredOptionsList: ICaoAutoCompleteOption[];
|
|
138
147
|
isFocused: boolean;
|
|
139
|
-
|
|
148
|
+
isPanelOpen: boolean;
|
|
149
|
+
activeIndex: number;
|
|
150
|
+
private static uidSeq;
|
|
151
|
+
readonly listboxId: string;
|
|
140
152
|
private selectedOption;
|
|
141
153
|
private options$;
|
|
154
|
+
/** indica se o valor atual do internalControl foi digitado pelo usuário (e não escrito programaticamente) */
|
|
155
|
+
private isUserTyping;
|
|
142
156
|
private el;
|
|
157
|
+
private cdr;
|
|
158
|
+
private renderer;
|
|
159
|
+
private overlay;
|
|
160
|
+
private viewContainerRef;
|
|
161
|
+
private scrollDispatcher;
|
|
162
|
+
private sub;
|
|
163
|
+
private overlayRef;
|
|
164
|
+
private portal;
|
|
143
165
|
private onChange;
|
|
144
166
|
private onTouched;
|
|
145
167
|
ngOnInit(): void;
|
|
146
|
-
|
|
168
|
+
ngAfterViewInit(): void;
|
|
147
169
|
ngOnChanges(changes: SimpleChanges): void;
|
|
170
|
+
ngOnDestroy(): void;
|
|
171
|
+
private setupOverlay;
|
|
172
|
+
private closeOverlay;
|
|
173
|
+
private openOverlay;
|
|
174
|
+
private setStyle;
|
|
175
|
+
openPanel(): void;
|
|
176
|
+
closePanel(): void;
|
|
177
|
+
onDocumentClick(event: MouseEvent): void;
|
|
148
178
|
onFocus(): void;
|
|
149
179
|
onBlur(): void;
|
|
180
|
+
onInputKeydown(event: KeyboardEvent): void;
|
|
181
|
+
private moveActiveIndex;
|
|
150
182
|
selectItem(option: ICaoAutoCompleteOption): void;
|
|
151
|
-
onOptionSelected(event: MatAutocompleteSelectedEvent): void;
|
|
152
183
|
selectOption(option: ICaoAutoCompleteOption): void;
|
|
153
|
-
closePanel(): void;
|
|
154
184
|
clearSelection(): void;
|
|
155
185
|
onLeadingIconClick(event: MouseEvent | Event): void;
|
|
156
186
|
onTrailingIconClick(event: MouseEvent | Event): void;
|
|
157
187
|
private filter;
|
|
188
|
+
/** Move a opção selecionada para o início da lista, mantendo a ordem das demais */
|
|
189
|
+
private moveSelectedToTop;
|
|
190
|
+
private writeDisplayValue;
|
|
191
|
+
/** Marca que o usuário está digitando manualmente no campo (ativa o filtro por texto) */
|
|
192
|
+
onUserInput(): void;
|
|
158
193
|
displayFn(option: ICaoAutoCompleteOption): string;
|
|
194
|
+
isOptionSelected(option: ICaoAutoCompleteOption): boolean;
|
|
195
|
+
trackByOptionId(_: number, option: ICaoAutoCompleteOption): string | number;
|
|
196
|
+
optionId(option: ICaoAutoCompleteOption): string;
|
|
197
|
+
get activeOptionId(): string | null;
|
|
159
198
|
get errorText(): string | null;
|
|
160
199
|
get controlNameValue(): string;
|
|
161
200
|
writeValue(value: ICaoAutoCompleteOption | string): void;
|
|
162
201
|
registerOnChange(fn: (value: ICaoAutoCompleteOption | string | null) => void): void;
|
|
163
202
|
registerOnTouched(fn: () => void): void;
|
|
164
|
-
|
|
203
|
+
setDisabledState?(isDisabled: boolean): void;
|
|
204
|
+
setInputHeight(): void;
|
|
165
205
|
static ɵfac: i0.ɵɵFactoryDeclaration<CaoAutocompleteComponent, never>;
|
|
166
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<CaoAutocompleteComponent, "cao-autocomplete", never, { "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "options": { "alias": "options"; "required": false; }; "control": { "alias": "control"; "required": false; }; "dataCy": { "alias": "dataCy"; "required": false; }; "controlName": { "alias": "controlName"; "required": false; }; "isLucideIcon": { "alias": "isLucideIcon"; "required": false; }; "iconColor": { "alias": "iconColor"; "required": false; }; "_leadingIcon": { "alias": "_leadingIcon"; "required": false; }; "_trailingIcon": { "alias": "_trailingIcon"; "required": false; }; "optionIcon": { "alias": "optionIcon"; "required": false; }; "optionIConColor": { "alias": "optionIConColor"; "required": false; }; "isLucideOptionIcon": { "alias": "isLucideOptionIcon"; "required": false; }; "autocompleteBgColor": { "alias": "autocompleteBgColor"; "required": false; }; "strokeWidthIcon": { "alias": "strokeWidthIcon"; "required": false; }; "isDisabled": { "alias": "isDisabled"; "required": false; }; }, { "itemSelected": "itemSelected"; "blurEvent": "blurEvent"; "focusEvent": "focusEvent"; "leadingIconClick": "leadingIconClick"; "trailingIconClick": "trailingIconClick"; }, never, never, true, never>;
|
|
206
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CaoAutocompleteComponent, "cao-autocomplete", never, { "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "options": { "alias": "options"; "required": false; }; "control": { "alias": "control"; "required": false; }; "dataCy": { "alias": "dataCy"; "required": false; }; "controlName": { "alias": "controlName"; "required": false; }; "isLucideIcon": { "alias": "isLucideIcon"; "required": false; }; "iconColor": { "alias": "iconColor"; "required": false; }; "_leadingIcon": { "alias": "_leadingIcon"; "required": false; }; "_trailingIcon": { "alias": "_trailingIcon"; "required": false; }; "optionIcon": { "alias": "optionIcon"; "required": false; }; "optionIConColor": { "alias": "optionIConColor"; "required": false; }; "isLucideOptionIcon": { "alias": "isLucideOptionIcon"; "required": false; }; "autocompleteBgColor": { "alias": "autocompleteBgColor"; "required": false; }; "strokeWidthIcon": { "alias": "strokeWidthIcon"; "required": false; }; "isDisabled": { "alias": "isDisabled"; "required": false; }; "inputHeigth": { "alias": "inputHeigth"; "required": false; }; "iconSize": { "alias": "iconSize"; "required": false; }; "checkIcon": { "alias": "checkIcon"; "required": false; }; "showCheckIcon": { "alias": "showCheckIcon"; "required": false; }; "isLucideCheckIcon": { "alias": "isLucideCheckIcon"; "required": false; }; }, { "itemSelected": "itemSelected"; "blurEvent": "blurEvent"; "focusEvent": "focusEvent"; "leadingIconClick": "leadingIconClick"; "trailingIconClick": "trailingIconClick"; }, never, never, true, never>;
|
|
167
207
|
}
|
|
168
208
|
|
|
169
209
|
interface ICaoDropDownOption {
|
|
@@ -403,6 +443,23 @@ declare class CaoBaseIcon {
|
|
|
403
443
|
static ɵcmp: i0.ɵɵComponentDeclaration<CaoBaseIcon, "cao-base-icon", never, { "imgSource": { "alias": "imgSource"; "required": false; }; "isLucideIcon": { "alias": "isLucideIcon"; "required": false; }; "strokeWidthIcon": { "alias": "strokeWidthIcon"; "required": false; }; "iconColor": { "alias": "iconColor"; "required": false; }; "size": { "alias": "size"; "required": false; }; }, {}, never, never, true, never>;
|
|
404
444
|
}
|
|
405
445
|
|
|
446
|
+
interface ICaoImage {
|
|
447
|
+
src: string;
|
|
448
|
+
alt: string;
|
|
449
|
+
}
|
|
450
|
+
interface ICaoKPI {
|
|
451
|
+
title: string;
|
|
452
|
+
value: string;
|
|
453
|
+
textColor: string;
|
|
454
|
+
borderColor: string;
|
|
455
|
+
icon: ICaoImage;
|
|
456
|
+
}
|
|
457
|
+
declare class CaoKpi {
|
|
458
|
+
readonly kpiData: i0.InputSignal<ICaoKPI>;
|
|
459
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CaoKpi, never>;
|
|
460
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CaoKpi, "cao-kpi", never, { "kpiData": { "alias": "kpiData"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
461
|
+
}
|
|
462
|
+
|
|
406
463
|
type ModalType = "validation" | "finger" | "facial" | "clinic" | "graph" | "restriction" | "default";
|
|
407
464
|
interface IModal {
|
|
408
465
|
status: boolean;
|
|
@@ -795,5 +852,5 @@ declare class BaseAlertService {
|
|
|
795
852
|
static ɵprov: i0.ɵɵInjectableDeclaration<BaseAlertService>;
|
|
796
853
|
}
|
|
797
854
|
|
|
798
|
-
export { BaseAlert, BaseAlertService, BaseDialog, BaseDialogService, BaseFilterTablePaginatedService, BaseFilterTableService, BaseModal, BaseModalService, BasePaginationService, BaseTable, BaseTablePaginated, BaseTableService, BaseTimeRange, CaoAutocompleteComponent, CaoBaseButtonComponent, CaoBaseIcon, CaoBaseInput, CaoDropDownComponent, CaoTabGroup, CaoTabPanel, Footer, LinksButton, Paginator, StepComponent, StepperComponent, TablePaginatedService, TimePicker, getPtBrPaginatorIntlBasePaginated, getPtBrPaginatorIntlBaseTable };
|
|
799
|
-
export type { AlertType, CaoTypeInputs, DialogType, IAlertConfig, ICaoAutoCompleteOption, ICaoDropDownOption, ICaoTabChangeEvent, IColumnButton, IColumnConfig, IColumnPaginatedButton, IColumnPaginatedConfig, IDialogConfig, IModal, INavContent, INavLink, ITableConfig, ITablePaginatedConfig, ModalType, ModalTypeClass, ModalTypeSize };
|
|
855
|
+
export { BaseAlert, BaseAlertService, BaseDialog, BaseDialogService, BaseFilterTablePaginatedService, BaseFilterTableService, BaseModal, BaseModalService, BasePaginationService, BaseTable, BaseTablePaginated, BaseTableService, BaseTimeRange, CaoAutocompleteComponent, CaoBaseButtonComponent, CaoBaseIcon, CaoBaseInput, CaoDropDownComponent, CaoKpi, CaoTabGroup, CaoTabPanel, Footer, LinksButton, Paginator, StepComponent, StepperComponent, TablePaginatedService, TimePicker, getPtBrPaginatorIntlBasePaginated, getPtBrPaginatorIntlBaseTable };
|
|
856
|
+
export type { AlertType, CaoTypeInputs, DialogType, IAlertConfig, ICaoAutoCompleteOption, ICaoDropDownOption, ICaoImage, ICaoKPI, ICaoTabChangeEvent, IColumnButton, IColumnConfig, IColumnPaginatedButton, IColumnPaginatedConfig, IDialogConfig, IModal, INavContent, INavLink, ITableConfig, ITablePaginatedConfig, ModalType, ModalTypeClass, ModalTypeSize };
|