ngx-opalbytes-components 1.26.0 → 1.28.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 +370 -1
- package/fesm2022/ngx-opalbytes-components.mjs +562 -3
- package/fesm2022/ngx-opalbytes-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/shared/components/accordion/accordion.component.css +30 -0
- package/src/lib/shared/components/accordion/accordion.component.html +1 -0
- package/src/lib/shared/components/accordion/accordion.component.spec.ts +117 -0
- package/src/lib/shared/components/accordion/accordion.component.ts +195 -0
- package/src/lib/shared/components/accordion/accordion.token.ts +40 -0
- package/src/lib/shared/components/accordion-item/accordion-item.component.css +137 -0
- package/src/lib/shared/components/accordion-item/accordion-item.component.html +52 -0
- package/src/lib/shared/components/accordion-item/accordion-item.component.ts +157 -0
- package/src/lib/shared/components/accordion-item/accordion-item.spec.ts +22 -0
- package/src/lib/shared/components/checkbox/checkbox.component.css +131 -0
- package/src/lib/shared/components/checkbox/checkbox.component.html +84 -0
- package/src/lib/shared/components/checkbox/checkbox.component.spec.ts +344 -0
- package/src/lib/shared/components/checkbox/checkbox.component.ts +258 -0
- package/src/lib/shared/components/checkbox/checkbox.stories.ts +240 -0
- package/src/lib/shared/components/index.ts +4 -0
- package/types/ngx-opalbytes-components.d.ts +341 -126
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
computed,
|
|
5
|
+
DestroyRef,
|
|
6
|
+
effect,
|
|
7
|
+
HostBinding,
|
|
8
|
+
inject,
|
|
9
|
+
input,
|
|
10
|
+
model,
|
|
11
|
+
output,
|
|
12
|
+
untracked,
|
|
13
|
+
} from '@angular/core';
|
|
14
|
+
|
|
15
|
+
import { CAO_ACCORDION, CaoAccordionAlign, CaoAccordionTogglePosition, ICaoAccordionItemRef } from '../accordion/accordion.token';
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
let nextId = 0;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Item do accordion. Pode ser usado dentro de `<cao-accordion>` ou isolado (como um cartão expansível).
|
|
22
|
+
* Os inputs de cor/posição, quando informados, sobrescrevem os valores definidos no accordion.
|
|
23
|
+
*/
|
|
24
|
+
@Component({
|
|
25
|
+
selector: 'cao-accordion-item',
|
|
26
|
+
templateUrl: './accordion-item.component.html',
|
|
27
|
+
styleUrl: './accordion-item.component.css',
|
|
28
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
29
|
+
})
|
|
30
|
+
export class CaoAccordionItemComponent implements ICaoAccordionItemRef {
|
|
31
|
+
private readonly accordion = inject(CAO_ACCORDION, { optional: true });
|
|
32
|
+
private readonly destroyRef = inject(DestroyRef);
|
|
33
|
+
private previousExpanded: boolean | null = null;
|
|
34
|
+
|
|
35
|
+
protected readonly headerId = `cao-accordion-header-${nextId}`;
|
|
36
|
+
protected readonly panelId = `cao-accordion-panel-${nextId++}`;
|
|
37
|
+
|
|
38
|
+
// ----- Conteúdo -----
|
|
39
|
+
readonly label = input<string>('');
|
|
40
|
+
readonly description = input<string>('');
|
|
41
|
+
/** Nível do heading (aria-level) do cabeçalho. */
|
|
42
|
+
readonly headingLevel = input<number>(3);
|
|
43
|
+
|
|
44
|
+
// ----- Estado -----
|
|
45
|
+
/** Estado de expansão. Suporta two-way binding: `[(expanded)]`. */
|
|
46
|
+
readonly expanded = model<boolean>(false);
|
|
47
|
+
readonly isDisabled = input<boolean>(false);
|
|
48
|
+
/** Exibe ou oculta o item. */
|
|
49
|
+
readonly isVisible = input<boolean>(true);
|
|
50
|
+
readonly dataCy = input<string>('');
|
|
51
|
+
|
|
52
|
+
// ----- Posição (sobrescreve o accordion quando informado) -----
|
|
53
|
+
readonly hideToggle = input<boolean | undefined>(undefined);
|
|
54
|
+
readonly togglePosition = input<CaoAccordionTogglePosition | undefined>(undefined);
|
|
55
|
+
readonly headerAlign = input<CaoAccordionAlign | undefined>(undefined);
|
|
56
|
+
|
|
57
|
+
// ----- Cores (sobrescrevem o accordion quando informadas) -----
|
|
58
|
+
readonly headerBgColor = input<string>('');
|
|
59
|
+
readonly headerTextColor = input<string>('');
|
|
60
|
+
readonly headerActiveBgColor = input<string>('');
|
|
61
|
+
readonly activeColor = input<string>('');
|
|
62
|
+
readonly contentBgColor = input<string>('');
|
|
63
|
+
readonly contentTextColor = input<string>('');
|
|
64
|
+
readonly borderColor = input<string>('');
|
|
65
|
+
readonly iconColor = input<string>('');
|
|
66
|
+
|
|
67
|
+
// ----- Eventos -----
|
|
68
|
+
readonly opened = output<void>();
|
|
69
|
+
readonly closed = output<void>();
|
|
70
|
+
|
|
71
|
+
protected readonly toggleHidden = computed(
|
|
72
|
+
() => this.hideToggle() ?? this.accordion?.hideToggle() ?? false,
|
|
73
|
+
);
|
|
74
|
+
protected readonly togglePos = computed(
|
|
75
|
+
() => this.togglePosition() ?? this.accordion?.togglePosition() ?? 'after',
|
|
76
|
+
);
|
|
77
|
+
private readonly align = computed(() => this.headerAlign() ?? this.accordion?.headerAlign() ?? 'start');
|
|
78
|
+
private readonly isCard = computed(() => (this.accordion?.displayMode() ?? 'separated') === 'separated');
|
|
79
|
+
|
|
80
|
+
protected readonly headerDataCy = computed(() => (this.dataCy() ? `${this.dataCy()}-header` : null));
|
|
81
|
+
protected readonly panelDataCy = computed(() => (this.dataCy() ? `${this.dataCy()}-content` : null));
|
|
82
|
+
|
|
83
|
+
private readonly hostStyle = computed(() => ({
|
|
84
|
+
'--cao-accordion-header-bg': this.headerBgColor() || null,
|
|
85
|
+
'--cao-accordion-header-text': this.headerTextColor() || null,
|
|
86
|
+
'--cao-accordion-header-active-bg': this.headerActiveBgColor() || null,
|
|
87
|
+
'--cao-accordion-active-color': this.activeColor() || null,
|
|
88
|
+
'--cao-accordion-content-bg': this.contentBgColor() || null,
|
|
89
|
+
'--cao-accordion-content-text': this.contentTextColor() || null,
|
|
90
|
+
'--cao-accordion-border-color': this.borderColor() || null,
|
|
91
|
+
'--cao-accordion-icon-color': this.iconColor() || null,
|
|
92
|
+
'--cao-accordion-header-align': this.align(),
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
@HostBinding('style')
|
|
96
|
+
get styleBinding(): Record<string, string | null> {
|
|
97
|
+
return this.hostStyle();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@HostBinding('attr.data-cy')
|
|
101
|
+
get dataCyAttr(): string | null {
|
|
102
|
+
return this.dataCy() || null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@HostBinding('class.cao-accordion-item--hidden')
|
|
106
|
+
get hiddenClass(): boolean {
|
|
107
|
+
return !this.isVisible();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@HostBinding('class.cao-accordion-item--expanded')
|
|
111
|
+
get expandedClass(): boolean {
|
|
112
|
+
return this.expanded();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@HostBinding('class.cao-accordion-item--card')
|
|
116
|
+
get cardClass(): boolean {
|
|
117
|
+
return this.isCard();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
constructor() {
|
|
121
|
+
this.accordion?.register(this);
|
|
122
|
+
this.destroyRef.onDestroy(() => this.accordion?.unregister(this));
|
|
123
|
+
|
|
124
|
+
effect(() => {
|
|
125
|
+
const isExpanded = this.expanded();
|
|
126
|
+
untracked(() => this.handleExpandedChange(isExpanded));
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Alterna o estado (ignorado quando o item está desabilitado). */
|
|
131
|
+
toggle(): void {
|
|
132
|
+
if (this.isDisabled()) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.expanded.update((value) => !value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
open(): void {
|
|
139
|
+
this.expanded.set(true);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
close(): void {
|
|
143
|
+
this.expanded.set(false);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private handleExpandedChange(isExpanded: boolean): void {
|
|
147
|
+
if (isExpanded) {
|
|
148
|
+
this.accordion?.notifyOpened(this);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.previousExpanded !== null && this.previousExpanded !== isExpanded) {
|
|
152
|
+
const event = isExpanded ? this.opened : this.closed;
|
|
153
|
+
event.emit();
|
|
154
|
+
}
|
|
155
|
+
this.previousExpanded = isExpanded;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
import { CaoAccordionItemComponent } from './accordion-item.component';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
describe('AccordionItem', () => {
|
|
6
|
+
let component: CaoAccordionItemComponent;
|
|
7
|
+
let fixture: ComponentFixture<CaoAccordionItemComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [CaoAccordionItemComponent],
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
|
|
14
|
+
fixture = TestBed.createComponent(CaoAccordionItemComponent);
|
|
15
|
+
component = fixture.componentInstance;
|
|
16
|
+
await fixture.whenStable();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should create', () => {
|
|
20
|
+
expect(component).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
--cao-checkbox-color: #4f46e5;
|
|
3
|
+
--cao-checkbox-unselected: #6b7280;
|
|
4
|
+
--cao-checkbox-disabled: #d1d5db;
|
|
5
|
+
--cao-checkbox-label: #111827;
|
|
6
|
+
--cao-checkbox-label-disabled: #9ca3af;
|
|
7
|
+
--cao-checkbox-focus-ring: rgba(79, 70, 229, 0.35);
|
|
8
|
+
--cao-checkbox-size: 20px;
|
|
9
|
+
--cao-checkbox-radius: 4px;
|
|
10
|
+
--cao-checkbox-duration: 200ms;
|
|
11
|
+
--cao-checkbox-icon-size: 14px;
|
|
12
|
+
--cao-checkbox-icon-color: #ffffff;
|
|
13
|
+
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.cao-checkbox {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: 8px;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
user-select: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.cao-checkbox--disabled {
|
|
26
|
+
cursor: not-allowed;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.cao-checkbox__input {
|
|
30
|
+
position: absolute;
|
|
31
|
+
width: 1px;
|
|
32
|
+
height: 1px;
|
|
33
|
+
margin: -1px;
|
|
34
|
+
padding: 0;
|
|
35
|
+
border: 0;
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
clip: rect(0 0 0 0);
|
|
38
|
+
clip-path: inset(50%);
|
|
39
|
+
white-space: nowrap;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.cao-checkbox__control {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
flex: none;
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
width: var(--cao-checkbox-size);
|
|
49
|
+
height: var(--cao-checkbox-size);
|
|
50
|
+
border: 2px solid var(--cao-checkbox-unselected);
|
|
51
|
+
border-radius: var(--cao-checkbox-radius);
|
|
52
|
+
background-color: transparent;
|
|
53
|
+
transition:
|
|
54
|
+
background-color var(--cao-checkbox-duration) ease,
|
|
55
|
+
border-color var(--cao-checkbox-duration) ease;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.cao-checkbox__icon {
|
|
59
|
+
flex: none;
|
|
60
|
+
width: var(--cao-checkbox-icon-size);
|
|
61
|
+
height: var(--cao-checkbox-icon-size);
|
|
62
|
+
color: var(--cao-checkbox-icon-color);
|
|
63
|
+
pointer-events: none;
|
|
64
|
+
object-fit: contain;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.cao-checkbox__icon--default {
|
|
68
|
+
fill: none;
|
|
69
|
+
stroke: currentColor;
|
|
70
|
+
stroke-width: 2.5;
|
|
71
|
+
stroke-linecap: round;
|
|
72
|
+
stroke-linejoin: round;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.cao-checkbox__label {
|
|
76
|
+
color: var(--cao-checkbox-label);
|
|
77
|
+
font-size: 0.875rem;
|
|
78
|
+
line-height: 1.25rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.cao-checkbox__label:empty {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.cao-checkbox:not(.cao-checkbox--disabled):hover .cao-checkbox__control {
|
|
86
|
+
border-color: var(--cao-checkbox-color);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.cao-checkbox--checked .cao-checkbox__control,
|
|
90
|
+
.cao-checkbox--indeterminate .cao-checkbox__control {
|
|
91
|
+
border-color: var(--cao-checkbox-color);
|
|
92
|
+
background-color: var(--cao-checkbox-color);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.cao-checkbox--invalid .cao-checkbox__control {
|
|
96
|
+
border-color: var(--error, #dc2626);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.cao-checkbox--valid .cao-checkbox__control {
|
|
100
|
+
border-color: var(--success, #16a34a);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.cao-checkbox--disabled .cao-checkbox__control {
|
|
104
|
+
border-color: var(--cao-checkbox-disabled);
|
|
105
|
+
background-color: transparent;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.cao-checkbox--disabled.cao-checkbox--checked .cao-checkbox__control,
|
|
109
|
+
.cao-checkbox--disabled.cao-checkbox--indeterminate .cao-checkbox__control {
|
|
110
|
+
background-color: var(--cao-checkbox-disabled);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.cao-checkbox--disabled .cao-checkbox__icon {
|
|
114
|
+
opacity: 0.65;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.cao-checkbox--disabled .cao-checkbox__label {
|
|
118
|
+
color: var(--cao-checkbox-label-disabled);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.cao-checkbox__input:focus-visible + .cao-checkbox__control {
|
|
122
|
+
box-shadow: 0 0 0 3px var(--cao-checkbox-focus-ring);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.cao-checkbox--label-before .cao-checkbox__control {
|
|
126
|
+
order: 2;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.cao-checkbox--label-before .cao-checkbox__label {
|
|
130
|
+
order: 1;
|
|
131
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<label
|
|
2
|
+
class="cao-checkbox"
|
|
3
|
+
[class.cao-checkbox--checked]="checked && !indeterminate"
|
|
4
|
+
[class.cao-checkbox--indeterminate]="indeterminate"
|
|
5
|
+
[class.cao-checkbox--disabled]="isDisabled"
|
|
6
|
+
[class.cao-checkbox--label-before]="labelPosition === 'before'"
|
|
7
|
+
[class.cao-checkbox--invalid]="hasInvalidState"
|
|
8
|
+
[class.cao-checkbox--valid]="hasValidState">
|
|
9
|
+
<input
|
|
10
|
+
#input
|
|
11
|
+
class="cao-checkbox__input"
|
|
12
|
+
type="checkbox"
|
|
13
|
+
[checked]="checked"
|
|
14
|
+
[indeterminate]="indeterminate"
|
|
15
|
+
[disabled]="isDisabled"
|
|
16
|
+
[required]="required"
|
|
17
|
+
[attr.name]="name || null"
|
|
18
|
+
[attr.id]="inputId"
|
|
19
|
+
[attr.aria-label]="ariaLabel || null"
|
|
20
|
+
[attr.aria-labelledby]="ariaLabelledBy || null"
|
|
21
|
+
[attr.data-cy]="dataCy || null"
|
|
22
|
+
(change)="onInputChange($event)"
|
|
23
|
+
(blur)="onBlur()" />
|
|
24
|
+
|
|
25
|
+
<span class="cao-checkbox__control" aria-hidden="true">
|
|
26
|
+
@if (indeterminate) {
|
|
27
|
+
@if (indeterminateIcon) {
|
|
28
|
+
@if (isLucideIcon) {
|
|
29
|
+
<svg
|
|
30
|
+
class="cao-checkbox__icon"
|
|
31
|
+
[lucideIcon]="indeterminateIcon"
|
|
32
|
+
[size]="iconSize"
|
|
33
|
+
[strokeWidth]="iconStrokeWidth"></svg>
|
|
34
|
+
} @else {
|
|
35
|
+
<img class="cao-checkbox__icon" alt="" [src]="indeterminateIcon" />
|
|
36
|
+
}
|
|
37
|
+
} @else {
|
|
38
|
+
<svg
|
|
39
|
+
class="cao-checkbox__icon cao-checkbox__icon--default"
|
|
40
|
+
viewBox="0 0 24 24"
|
|
41
|
+
aria-hidden="true">
|
|
42
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
43
|
+
</svg>
|
|
44
|
+
}
|
|
45
|
+
} @else if (checked) {
|
|
46
|
+
@if (checkedIcon) {
|
|
47
|
+
@if (isLucideIcon) {
|
|
48
|
+
<svg
|
|
49
|
+
class="cao-checkbox__icon"
|
|
50
|
+
[lucideIcon]="checkedIcon"
|
|
51
|
+
[size]="iconSize"
|
|
52
|
+
[strokeWidth]="iconStrokeWidth"></svg>
|
|
53
|
+
} @else {
|
|
54
|
+
<img class="cao-checkbox__icon" alt="" [src]="checkedIcon" />
|
|
55
|
+
}
|
|
56
|
+
} @else {
|
|
57
|
+
<svg
|
|
58
|
+
class="cao-checkbox__icon cao-checkbox__icon--default"
|
|
59
|
+
viewBox="0 0 24 24"
|
|
60
|
+
aria-hidden="true">
|
|
61
|
+
<polyline points="20 6 9 17 4 12" />
|
|
62
|
+
</svg>
|
|
63
|
+
}
|
|
64
|
+
} @else if (uncheckedIcon) {
|
|
65
|
+
@if (isLucideIcon) {
|
|
66
|
+
<svg
|
|
67
|
+
class="cao-checkbox__icon"
|
|
68
|
+
[lucideIcon]="uncheckedIcon"
|
|
69
|
+
[size]="iconSize"
|
|
70
|
+
[strokeWidth]="iconStrokeWidth"></svg>
|
|
71
|
+
} @else {
|
|
72
|
+
<img class="cao-checkbox__icon" alt="" [src]="uncheckedIcon" />
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</span>
|
|
76
|
+
|
|
77
|
+
<span class="cao-checkbox__label">
|
|
78
|
+
@if (label) {
|
|
79
|
+
{{ label }}
|
|
80
|
+
} @else {
|
|
81
|
+
<ng-content />
|
|
82
|
+
}
|
|
83
|
+
</span>
|
|
84
|
+
</label>
|