Skip to content

Commit 6fe3c81

Browse files
committed
add filters
1 parent fc88962 commit 6fe3c81

7 files changed

Lines changed: 311 additions & 81 deletions

File tree

projects/some-angular-utils/filter/src/lib/components/custom-input/custom-input.component.html

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
@if(formControlItem){
2-
<div class="sau-input" [class.sau-input--slide-tri-state]="type === 'checkbox'">
1+
@if (formControlItem) {
2+
<div class="sau-input" [class.sau-input--slide-tri-state]="type === 'checkbox'" (click)="toggleTriState($event)">
33

44
@if (type !== 'checkbox') {
55
<label class="sau-input__label">
@@ -13,28 +13,32 @@
1313
<div class="sau-input__slider-container">
1414
<div class="sau-input__slider-track" [class.is-true]="inputControl.value === true"
1515
[class.is-false]="inputControl.value === false"
16-
[class.is-unspecified]="inputControl.value === null || inputControl.value === undefined"
17-
(click)="toggleTriState($event)">
16+
[class.is-unspecified]="inputControl.value === null || inputControl.value === undefined">
1817

1918
<div class="sau-input__slider-handle">
20-
@if (inputControl.value === true) { <span class="sau-input__handle-icon"></span> }
21-
@if (inputControl.value === false) { <span class="sau-input__handle-icon"></span> }
22-
@if (inputControl.value === null || inputControl.value === undefined) { <span
23-
class="sau-input__handle-icon">-</span> }
19+
@if (inputControl.value === true) {
20+
<span class="sau-input__handle-icon">{{ trueIcon }}</span>
21+
}
22+
@if (inputControl.value === false) {
23+
<span class="sau-input__handle-icon">{{ falseIcon }}</span>
24+
}
25+
@if (inputControl.value === null || inputControl.value === undefined) {
26+
<span class="sau-input__handle-icon">{{ nullIcon }}</span>
27+
}
2428
</div>
2529
</div>
2630

2731
<span class="sau-input__slider-status-text">
28-
@if (inputControl.value === true) { Activo }
29-
@if (inputControl.value === false) { Inactivo }
30-
@if (inputControl.value === null || inputControl.value === undefined) { Sin especificar }
32+
@if (inputControl.value === true) { {{ trueLabel }} }
33+
@if (inputControl.value === false) { {{ falseLabel }} }
34+
@if (inputControl.value === null || inputControl.value === undefined) { {{ nullLabel }} }
3135
</span>
3236
</div>
3337
</div>
3438
}
3539

36-
@if(formControlItem.invalid && (formControlItem.touched || formControlItem.dirty)){
37-
<div class="sau-input__error">
40+
@if (formControlItem.invalid && (formControlItem.touched || formControlItem.dirty)) {
41+
<div class="sau-filter__error">
3842
Campo obligatorio
3943
</div>
4044
}

projects/some-angular-utils/filter/src/lib/components/custom-input/custom-input.component.scss

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
letter-spacing: -0.025em;
77
width: 100%;
88
position: relative;
9+
cursor: pointer;
910

1011
&__label {
1112
font-weight: bold;
@@ -48,13 +49,16 @@
4849
}
4950
}
5051

51-
/* --- ARQUITECTURA DEL SLIDE TRI-STATE COMPACTO --- */
52+
/* ==========================================================================
53+
ARQUITECTURA DEL SLIDE TRI-STATE COMPACTO (FILTROS ESTÁNDAR)
54+
========================================================================== */
5255
&__slider-container {
5356
display: flex;
5457
align-items: center;
5558
gap: 0.65rem;
5659
min-height: 38px;
5760
box-sizing: border-box;
61+
width: 9rem;
5862
}
5963

6064
&__slider-track {
@@ -68,16 +72,17 @@
6872
border: 1px solid #cbd5e1;
6973
box-sizing: border-box;
7074

71-
&.is-false {
72-
background-color: #fca5a5;
73-
border-color: #f87171;
74-
}
75-
7675
&.is-unspecified {
7776
background-color: #e2e8f0;
7877
border-color: #cbd5e1;
7978
}
8079

80+
/* Color rojo original para cuando algo está inactivo/falso */
81+
&.is-false {
82+
background-color: #fca5a5;
83+
border-color: #f87171;
84+
}
85+
8186
&.is-true {
8287
background-color: rgb(233, 213, 255);
8388
border-color: rgb(168, 85, 247);
@@ -101,29 +106,24 @@
101106
.is-false & {
102107
transform: translateX(0px);
103108
color: #dc2626;
109+
/* Icono equis en rojo */
104110
}
105111

106112
.is-unspecified & {
107113
transform: translateX(16px);
108-
/* Centro exacto */
109114
color: #64748b;
110115
}
111116

112117
.is-true & {
113118
transform: translateX(32px);
114119
color: rgb(147, 51, 234);
115-
background-color: rgb(147, 51, 234);
116120
}
117121
}
118122

119123
&__handle-icon {
120124
font-size: 0.6rem;
121125
font-weight: bold;
122126
user-select: none;
123-
124-
.is-true & {
125-
color: #ffffff;
126-
}
127127
}
128128

129129
&__slider-status-text {

projects/some-angular-utils/filter/src/lib/components/custom-input/custom-input.component.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ export class CustomInputComponent {
1212
@Input() placeholder = '';
1313
@Input() type = 'text';
1414

15+
// Textos de estado
16+
@Input() trueLabel = 'Activo';
17+
@Input() falseLabel = 'Inactivo';
18+
@Input() nullLabel = 'Sin especificar';
19+
20+
// Nuevos inputs para personalizar los iconos del tirador (por defecto los de checkbox)
21+
@Input() trueIcon = '✓';
22+
@Input() falseIcon = '✕';
23+
@Input() nullIcon = '•';
24+
1525
private _formControlItem!: AbstractControl;
1626
inputControl!: FormControl;
1727

projects/some-angular-utils/filter/src/lib/filter.html

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,40 @@
5656
}
5757
}
5858

59-
<!-- Botón para aplicar y generar URL/JSON -->
60-
<button type="button" class="sau-filter__btn" (click)="processFilter()">
61-
{{searchButtonText}}
62-
</button>
59+
<div class="sau-filter__actions-wrapper">
60+
<div class="sau-filter__split-btn" [class.sau-filter__split-btn--simple]="!hasOrderFields()">
61+
<button type="button" class="sau-filter__btn-main" (click)="processFilter()">
62+
{{searchButtonText}}
63+
</button>
64+
65+
@if (hasOrderFields()) {
66+
<button type="button" class="sau-filter__btn-arrow" (click)="toggleOrderDropdown($event)">
67+
<svg [class.is-open]="showOrderDropdown" fill="none" viewBox="0 0 24 24" stroke="currentColor"
68+
stroke-width="2.5">
69+
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
70+
</svg>
71+
</button>
72+
}
73+
</div>
74+
75+
@if (showOrderDropdown && hasOrderFields()) {
76+
<div class="sau-filter__order-popover" (click)="$event.stopPropagation()">
77+
<div class="sau-filter__order-container">
78+
<span class="sau-filter__order-title">Criterios de Ordenación</span>
79+
80+
@for (option of filterConfig.orderByFields; track option.field) {
81+
<div class="sau-filter__order-row">
82+
<custom-input class="sau-filter__order-input" [label]="option.label" [type]="'checkbox'"
83+
[formControlItem]="sortOrderGroup.controls[option.field]" trueLabel="Ascendente"
84+
falseLabel="Descendente" nullLabel="Sin ordenar" trueIcon="" falseIcon="">
85+
</custom-input>
86+
</div>
87+
}
88+
</div>
89+
</div>
90+
}
91+
</div>
92+
6393
</div>
6494
</form>
6595
</div>

projects/some-angular-utils/filter/src/lib/filter.scss

Lines changed: 136 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
.sau-filter {
22
--sau-color-primary: rgb(147, 51, 234);
3-
--sau-color-secondary: var(--sau-color-primary);
43
--sau-color-background: rgb(255, 255, 255);
5-
--sau-color-edit: rgb(34, 197, 94);
6-
--sau-color-delete: rgb(239, 68, 68);
7-
--sau-color-text: rgb(31, 41, 55);
84

95
width: 100%;
106
display: block;
@@ -37,19 +33,11 @@
3733
width: 100%;
3834
}
3935

40-
&__btn {
41-
background-color: var(--sau-color-primary);
42-
color: var(--sau-color-background);
43-
font-size: 0.875rem;
44-
font-weight: bold;
45-
padding: 0.65rem 1.5rem;
46-
border: none;
47-
border-radius: 0.75rem;
48-
cursor: pointer;
49-
transition: background-color 0.2s ease-in-out, transform 0.1s ease;
50-
height: fit-content;
51-
white-space: nowrap;
52-
margin-left: auto;
36+
&__actions-wrapper {
37+
position: relative;
38+
width: 100%;
39+
display: flex;
40+
justify-content: flex-end;
5341
grid-column: auto;
5442

5543
@media (min-width: 576px) {
@@ -63,14 +51,141 @@
6351
@media (min-width: 1200px) {
6452
grid-column: 6;
6553
}
54+
}
55+
56+
&__split-btn {
57+
display: inline-flex;
58+
align-items: center;
59+
background-color: var(--sau-color-primary);
60+
border-radius: 0.75rem;
61+
overflow: hidden;
62+
63+
&:hover {
64+
opacity: 0.93;
65+
}
66+
67+
&--simple {
68+
border-radius: 0.75rem;
69+
70+
.sau-filter__btn-main {
71+
padding: 0.65rem 1.5rem;
72+
}
73+
}
74+
}
75+
76+
&__btn-main {
77+
background: none;
78+
color: var(--sau-color-background);
79+
font-size: 0.875rem;
80+
font-weight: bold;
81+
padding: 0.65rem 1.25rem;
82+
border: none;
83+
cursor: pointer;
84+
white-space: nowrap;
85+
}
86+
87+
&__btn-arrow {
88+
background: none;
89+
border: none;
90+
border-left: 1px solid rgba(255, 255, 255, 0.2);
91+
padding: 0.65rem 0.75rem;
92+
cursor: pointer;
93+
display: flex;
94+
align-items: center;
95+
justify-content: center;
96+
height: 100%;
97+
98+
svg {
99+
width: 0.85rem;
100+
height: 0.85rem;
101+
color: white;
102+
transition: transform 0.2s ease;
103+
104+
&.is-open {
105+
transform: rotate(180deg);
106+
}
107+
}
66108

67109
&:hover {
68-
background-color: var(--sau-color-primary);
69-
opacity: 0.85;
110+
background-color: rgba(255, 255, 255, 0.1);
111+
}
112+
}
113+
114+
/* --- PANEL FLOTANTE MULTI-REGISTRO --- */
115+
&__order-popover {
116+
position: absolute;
117+
top: 100%;
118+
right: 0;
119+
z-index: 60;
120+
margin-top: 0.5rem;
121+
background: #ffffff;
122+
border: 1px solid #e2e8f0;
123+
border-radius: 0.75rem;
124+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.05);
125+
padding: 1rem;
126+
width: 320px;
127+
box-sizing: border-box;
128+
}
129+
130+
&__order-container {
131+
display: flex;
132+
flex-direction: column;
133+
gap: 0.25rem;
134+
}
135+
136+
&__order-title {
137+
font-size: 0.75rem;
138+
text-transform: uppercase;
139+
font-weight: 700;
140+
color: #475569;
141+
letter-spacing: 0.05em;
142+
margin-bottom: 0.5rem;
143+
border-bottom: 1px solid #f1f5f9;
144+
padding-bottom: 0.5rem;
145+
}
146+
147+
&__order-row {
148+
width: 100%;
149+
padding: 0.25rem 0;
150+
border-bottom: 1px dashed #f1f5f9;
151+
152+
&:last-child {
153+
border-bottom: none;
70154
}
155+
}
156+
157+
/* ==========================================================================
158+
SOBREESCRITURA EXCLUSIVA PARA EL CONTROL DE ORDENACIÓN (MANTENE ROJO EN EL RESTO)
159+
========================================================================== */
160+
&__order-input {
161+
::ng-deep {
162+
.sau-input__label {
163+
flex-direction: row !important;
164+
align-items: center !important;
165+
justify-content: space-between !important;
166+
gap: 1rem;
167+
}
168+
169+
.sau-input__title {
170+
font-weight: 500;
171+
color: #1e293b;
172+
font-size: 0.8125rem;
173+
}
174+
175+
.sau-input__slider-container {
176+
min-height: auto;
177+
}
178+
179+
/* Forzamos que en la ordenación, el estado false use la paleta púrpura */
180+
.sau-input__slider-track.is-false {
181+
background-color: rgb(233, 213, 255) !important;
182+
border-color: rgb(168, 85, 247) !important;
183+
}
71184

72-
&:active {
73-
transform: scale(0.98);
185+
/* Forzamos el color del icono interno púrpura en vez de rojo */
186+
.sau-input__slider-track.is-false .sau-input__slider-handle {
187+
color: rgb(147, 51, 234) !important;
188+
}
74189
}
75190
}
76191
}

0 commit comments

Comments
 (0)