libey-ng-component-library 0.0.35 → 0.0.37
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/bundles/libey-ng-component-library.umd.js +2521 -0
- package/bundles/libey-ng-component-library.umd.js.map +1 -0
- package/esm2015/lib/cpm-indicator/libey-ng-cpm-indicator.component.js +193 -0
- package/esm2015/lib/cpm-indicator/libey-ng-cpm-indicator.module.js +26 -0
- package/esm2015/lib/icons/icons.module.js +26 -0
- package/esm2015/lib/icons/libey-ng-icons/libey-ng-icons.component.js +52 -0
- package/esm2015/lib/libey-ng-component-library.component.js +26 -0
- package/esm2015/lib/libey-ng-component-library.module.js +60 -0
- package/esm2015/lib/libey-ng-component-library.service.js +14 -0
- package/esm2015/lib/signature/libey-ng-signature/libey-ng-signature.component.js +197 -0
- package/esm2015/lib/signature/libey-ng-signature.module.js +57 -0
- package/esm2015/lib/table/directives/libey-column.directive.js +59 -0
- package/esm2015/lib/table/libey-ng-table.component.js +249 -0
- package/esm2015/lib/table/libey-ng-table.module.js +21 -0
- package/esm2015/libey-ng-component-library.js +5 -0
- package/esm2015/public-api.js +23 -0
- package/esm2015/utils/CpmIndicator/cpm-indicator-types.js +25 -0
- package/esm2015/utils/SigWeb/SigWeb.js +1442 -0
- package/fesm2015/libey-ng-component-library.js +2416 -0
- package/fesm2015/libey-ng-component-library.js.map +1 -0
- package/lib/cpm-indicator/libey-ng-cpm-indicator.component.d.ts +66 -0
- package/lib/cpm-indicator/libey-ng-cpm-indicator.module.d.ts +8 -0
- package/lib/icons/icons.module.d.ts +8 -0
- package/lib/icons/libey-ng-icons/libey-ng-icons.component.d.ts +19 -0
- package/lib/libey-ng-component-library.component.d.ts +8 -0
- package/lib/libey-ng-component-library.module.d.ts +13 -0
- package/lib/libey-ng-component-library.service.d.ts +6 -0
- package/lib/signature/libey-ng-signature/libey-ng-signature.component.d.ts +40 -0
- package/lib/signature/libey-ng-signature.module.d.ts +13 -0
- package/lib/table/directives/libey-column.directive.d.ts +24 -0
- package/lib/table/libey-ng-table.component.d.ts +64 -0
- package/lib/table/libey-ng-table.module.d.ts +10 -0
- package/libey-ng-component-library.d.ts +5 -0
- package/package.json +24 -17
- package/{src/public-api.ts → public-api.d.ts} +0 -4
- package/utils/CpmIndicator/cpm-indicator-types.d.ts +40 -0
- package/utils/SigWeb/SigWeb.d.ts +219 -0
- package/karma.conf.js +0 -44
- package/ng-package.json +0 -13
- package/src/lib/cpm-indicator/libey-ng-cpm-indicator.component.css +0 -216
- package/src/lib/cpm-indicator/libey-ng-cpm-indicator.component.html +0 -201
- package/src/lib/cpm-indicator/libey-ng-cpm-indicator.component.spec.ts +0 -25
- package/src/lib/cpm-indicator/libey-ng-cpm-indicator.component.ts +0 -203
- package/src/lib/cpm-indicator/libey-ng-cpm-indicator.module.ts +0 -19
- package/src/lib/icons/icons.component.css +0 -9
- package/src/lib/icons/icons.module.ts +0 -16
- package/src/lib/icons/libey-ng-icons/libey-ng-icons.component.html +0 -1831
- package/src/lib/icons/libey-ng-icons/libey-ng-icons.component.ts +0 -36
- package/src/lib/libey-ng-component-library.component.spec.ts +0 -25
- package/src/lib/libey-ng-component-library.component.ts +0 -20
- package/src/lib/libey-ng-component-library.module.ts +0 -32
- package/src/lib/libey-ng-component-library.service.spec.ts +0 -16
- package/src/lib/libey-ng-component-library.service.ts +0 -9
- package/src/lib/signature/libey-ng-signature/libey-ng-signature.component.css +0 -7
- package/src/lib/signature/libey-ng-signature/libey-ng-signature.component.html +0 -41
- package/src/lib/signature/libey-ng-signature/libey-ng-signature.component.spec.ts +0 -25
- package/src/lib/signature/libey-ng-signature/libey-ng-signature.component.ts +0 -214
- package/src/lib/signature/libey-ng-signature.module.ts +0 -30
- package/src/lib/table/directives/libey-column.directive.ts +0 -29
- package/src/lib/table/libey-ng-table.component.css +0 -249
- package/src/lib/table/libey-ng-table.component.html +0 -122
- package/src/lib/table/libey-ng-table.component.ts +0 -234
- package/src/lib/table/libey-ng-table.module.ts +0 -13
- package/src/test.ts +0 -28
- package/src/utils/CpmIndicator/cpm-indicator-types.ts +0 -63
- package/src/utils/SigWeb/SigWeb.ts +0 -1906
- package/tsconfig.lib.json +0 -20
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -17
package/ng-package.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
|
3
|
-
"dest": "../../dist/libey-ng-component-library",
|
|
4
|
-
"lib": {
|
|
5
|
-
"entryFile": "src/public-api.ts"
|
|
6
|
-
},
|
|
7
|
-
"whitelistedNonPeerDependencies": [
|
|
8
|
-
"angular2-signaturepad",
|
|
9
|
-
"@ng-select/ng-select",
|
|
10
|
-
"@ng-bootstrap/ng-bootstrap",
|
|
11
|
-
"@popperjs/core"
|
|
12
|
-
]
|
|
13
|
-
}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/* ── Host ── */
|
|
3
|
-
:host {
|
|
4
|
-
display: inline-flex;
|
|
5
|
-
flex-direction: column; /* ← agregar */
|
|
6
|
-
align-items: center; /* ← agregar */
|
|
7
|
-
font-family: 'DM Sans', sans-serif;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/* ═══════════════════════════════════════════════════════════
|
|
11
|
-
GAUGE — compartido entre modo CPM y modo Gauge
|
|
12
|
-
El modo CPM reutiliza todas estas clases, solo cambia
|
|
13
|
-
el texto central ("CPM" en lugar del monto)
|
|
14
|
-
═══════════════════════════════════════════════════════════ */
|
|
15
|
-
|
|
16
|
-
/* ── Tarjeta (solo modo Gauge) ── */
|
|
17
|
-
.card {
|
|
18
|
-
background: #ffffff;
|
|
19
|
-
border-radius: 20px;
|
|
20
|
-
padding: 36px 40px 28px;
|
|
21
|
-
width: 340px;
|
|
22
|
-
box-shadow:
|
|
23
|
-
0 4px 32px rgba(0, 0, 0, 0.10),
|
|
24
|
-
0 1px 4px rgba(0, 0, 0, 0.06);
|
|
25
|
-
display: flex;
|
|
26
|
-
flex-direction: column;
|
|
27
|
-
align-items: center;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/* ── Gauge SVG ── */
|
|
31
|
-
.gauge-wrap {
|
|
32
|
-
position: relative;
|
|
33
|
-
overflow: visible;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.gauge-svg {
|
|
37
|
-
overflow: visible;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* ── Valor central ── */
|
|
41
|
-
.gauge-center {
|
|
42
|
-
position: absolute;
|
|
43
|
-
left: 50%;
|
|
44
|
-
transform: translateX(-50%);
|
|
45
|
-
text-align: center;
|
|
46
|
-
pointer-events: none;
|
|
47
|
-
white-space: nowrap;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/* CPM — una sola línea, necesita más espacio */
|
|
51
|
-
.gauge-center--cpm {
|
|
52
|
-
bottom: 15px;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* Gauge — dos líneas (monto + límite) */
|
|
56
|
-
.gauge-center--gauge {
|
|
57
|
-
bottom: -10px;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.main-value {
|
|
61
|
-
font-size: 26px;
|
|
62
|
-
font-weight: 700;
|
|
63
|
-
font-family: 'DM Mono', monospace;
|
|
64
|
-
letter-spacing: -0.5px;
|
|
65
|
-
color: #1a1a2e;
|
|
66
|
-
line-height: 1;
|
|
67
|
-
transition: color 0.3s ease;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.limit-label {
|
|
71
|
-
font-size: 12px;
|
|
72
|
-
color: #8a8fa8;
|
|
73
|
-
font-weight: 400;
|
|
74
|
-
margin-top: 3px;
|
|
75
|
-
font-family: 'DM Mono', monospace;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/* ── Leyenda ── */
|
|
79
|
-
.legend {
|
|
80
|
-
display: flex;
|
|
81
|
-
gap: 14px;
|
|
82
|
-
margin-top: 22px;
|
|
83
|
-
margin-bottom: 6px;
|
|
84
|
-
flex-wrap: wrap;
|
|
85
|
-
justify-content: center;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.legend-item {
|
|
89
|
-
display: flex;
|
|
90
|
-
align-items: center;
|
|
91
|
-
gap: 6px;
|
|
92
|
-
font-size: 12px;
|
|
93
|
-
color: #555;
|
|
94
|
-
font-weight: 500;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/* ── Punto de leyenda ── */
|
|
98
|
-
.dot {
|
|
99
|
-
width: 9px;
|
|
100
|
-
height: 9px;
|
|
101
|
-
border-radius: 50%;
|
|
102
|
-
flex-shrink: 0;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/* ── Separador ── */
|
|
106
|
-
.divider {
|
|
107
|
-
width: 100%;
|
|
108
|
-
height: 1px;
|
|
109
|
-
background: #f0f2f5;
|
|
110
|
-
margin: 14px 0 4px;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* ── Badge dev mode ── */
|
|
114
|
-
.dev-badge {
|
|
115
|
-
font-size: 11px;
|
|
116
|
-
font-weight: 700;
|
|
117
|
-
color: #7c3aed;
|
|
118
|
-
background: #ede9fe;
|
|
119
|
-
border-radius: 6px;
|
|
120
|
-
padding: 3px 10px;
|
|
121
|
-
margin-bottom: 8px;
|
|
122
|
-
letter-spacing: 0.3px;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/* ── Controles devMode ── */
|
|
126
|
-
.controls {
|
|
127
|
-
width: 100%;
|
|
128
|
-
display: flex;
|
|
129
|
-
flex-direction: column;
|
|
130
|
-
gap: 16px;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.ctrl-row {
|
|
134
|
-
display: flex;
|
|
135
|
-
flex-direction: column;
|
|
136
|
-
gap: 5px;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.ctrl-header {
|
|
140
|
-
display: flex;
|
|
141
|
-
justify-content: space-between;
|
|
142
|
-
align-items: center;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.ctrl-label {
|
|
146
|
-
font-size: 12px;
|
|
147
|
-
color: #8a8fa8;
|
|
148
|
-
font-weight: 500;
|
|
149
|
-
display: flex;
|
|
150
|
-
align-items: center;
|
|
151
|
-
gap: 5px;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.ctrl-label-dot {
|
|
155
|
-
width: 8px;
|
|
156
|
-
height: 8px;
|
|
157
|
-
border-radius: 50%;
|
|
158
|
-
display: inline-block;
|
|
159
|
-
flex-shrink: 0;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.ctrl-val {
|
|
163
|
-
font-size: 12px;
|
|
164
|
-
font-family: 'DM Mono', monospace;
|
|
165
|
-
font-weight: 600;
|
|
166
|
-
color: #1a1a2e;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.zones-bar {
|
|
170
|
-
display: flex;
|
|
171
|
-
border-radius: 4px;
|
|
172
|
-
overflow: hidden;
|
|
173
|
-
height: 3px;
|
|
174
|
-
width: 100%;
|
|
175
|
-
margin-bottom: 3px;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.zones-bar div { flex: 1; }
|
|
179
|
-
|
|
180
|
-
/* ── Slider ── */
|
|
181
|
-
input[type='range'] {
|
|
182
|
-
-webkit-appearance: none;
|
|
183
|
-
appearance: none;
|
|
184
|
-
width: 100%;
|
|
185
|
-
height: 5px;
|
|
186
|
-
border-radius: 99px;
|
|
187
|
-
background: #e2e6ef;
|
|
188
|
-
outline: none;
|
|
189
|
-
cursor: pointer;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
input[type='range']::-webkit-slider-thumb {
|
|
193
|
-
-webkit-appearance: none;
|
|
194
|
-
width: 18px;
|
|
195
|
-
height: 18px;
|
|
196
|
-
border-radius: 50%;
|
|
197
|
-
background: #16a34a;
|
|
198
|
-
border: 2.5px solid #fff;
|
|
199
|
-
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2);
|
|
200
|
-
cursor: grab;
|
|
201
|
-
transition: background 0.3s, transform 0.15s;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
input[type='range']::-webkit-slider-thumb:active {
|
|
205
|
-
transform: scale(1.2);
|
|
206
|
-
cursor: grabbing;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
input[type='range']::-moz-range-thumb {
|
|
210
|
-
width: 18px;
|
|
211
|
-
height: 18px;
|
|
212
|
-
border-radius: 50%;
|
|
213
|
-
background: #16a34a;
|
|
214
|
-
border: 2.5px solid #fff;
|
|
215
|
-
cursor: grab;
|
|
216
|
-
}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
<!-- Modo CPM: solo el gauge con "CPM" en el centro -->
|
|
2
|
-
<ng-container *ngIf="mode === CpmIndicatorMode.Cpm">
|
|
3
|
-
<div class="gauge-wrap" [style.width.px]="220 * scale" [style.height.px]="120 * scale">
|
|
4
|
-
<svg class="gauge-svg" viewBox="0 0 220 130" xmlns="http://www.w3.org/2000/svg"
|
|
5
|
-
[style.width.px]="220 * scale"
|
|
6
|
-
[style.height.px]="135 * scale">
|
|
7
|
-
<defs>
|
|
8
|
-
<filter id="dotShadowCpm" x="-60%" y="-60%" width="220%" height="220%">
|
|
9
|
-
<feDropShadow dx="0" dy="1" stdDeviation="2" flood-color="rgba(0,0,0,0.22)"/>
|
|
10
|
-
</filter>
|
|
11
|
-
</defs>
|
|
12
|
-
|
|
13
|
-
<path [attr.d]="trackArcPath" fill="none" stroke="#E8EAEF" stroke-width="14" stroke-linecap="round"/>
|
|
14
|
-
|
|
15
|
-
<!-- Proyección debajo, real encima -->
|
|
16
|
-
<path
|
|
17
|
-
[style.display]="totalAmount > 0 ? '' : 'none'"
|
|
18
|
-
[attr.d]="totalArcPath"
|
|
19
|
-
[attr.stroke]="activeColors.light"
|
|
20
|
-
fill="none" stroke-width="14" stroke-linecap="round"/>
|
|
21
|
-
|
|
22
|
-
<path
|
|
23
|
-
[style.display]="baseAmount > 0 ? '' : 'none'"
|
|
24
|
-
[attr.d]="baseArcPath"
|
|
25
|
-
[attr.stroke]="activeColors.dark"
|
|
26
|
-
fill="none" stroke-width="14" stroke-linecap="round"/>
|
|
27
|
-
|
|
28
|
-
<circle
|
|
29
|
-
[style.display]="baseAmount > 0 ? '' : 'none'"
|
|
30
|
-
[attr.cx]="dotBasePos.x" [attr.cy]="dotBasePos.y"
|
|
31
|
-
r="7" [attr.fill]="activeColors.dark"
|
|
32
|
-
stroke="#ffffff" stroke-width="2.5"
|
|
33
|
-
filter="url(#dotShadowCpm)"/>
|
|
34
|
-
|
|
35
|
-
<!-- Solo visible si la proyección va por delante del real -->
|
|
36
|
-
<circle
|
|
37
|
-
[style.display]="dotTotalVisible ? '' : 'none'"
|
|
38
|
-
[attr.cx]="dotTotalPos.x" [attr.cy]="dotTotalPos.y"
|
|
39
|
-
r="6" [attr.fill]="activeColors.light"
|
|
40
|
-
stroke="#ffffff" stroke-width="2.5"
|
|
41
|
-
filter="url(#dotShadowCpm)"/>
|
|
42
|
-
</svg>
|
|
43
|
-
|
|
44
|
-
<div class="gauge-center gauge-center--cpm" [style.bottom.px]="15 * scale">
|
|
45
|
-
<div class="main-value" [style.font-size.px]="26 * scale">CPM</div>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
</ng-container>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<!-- Modo Gauge: gauge completo con montos, leyenda y alerta -->
|
|
52
|
-
<ng-container *ngIf="mode === CpmIndicatorMode.Gauge">
|
|
53
|
-
<div class="gauge-wrap" [style.width.px]="220 * scale" [style.height.px]="120 * scale">
|
|
54
|
-
<svg class="gauge-svg" viewBox="0 0 220 130" xmlns="http://www.w3.org/2000/svg"
|
|
55
|
-
[style.width.px]="220 * scale"
|
|
56
|
-
[style.height.px]="135 * scale">
|
|
57
|
-
<defs>
|
|
58
|
-
<filter id="dotShadowGauge" x="-60%" y="-60%" width="220%" height="220%">
|
|
59
|
-
<feDropShadow dx="0" dy="1" stdDeviation="2" flood-color="rgba(0,0,0,0.22)"/>
|
|
60
|
-
</filter>
|
|
61
|
-
</defs>
|
|
62
|
-
|
|
63
|
-
<path [attr.d]="trackArcPath" fill="none" stroke="#E8EAEF" stroke-width="14" stroke-linecap="round"/>
|
|
64
|
-
|
|
65
|
-
<!-- Proyección debajo, real encima -->
|
|
66
|
-
<path
|
|
67
|
-
[style.display]="totalAmount > 0 ? '' : 'none'"
|
|
68
|
-
[attr.d]="totalArcPath"
|
|
69
|
-
[attr.stroke]="activeColors.light"
|
|
70
|
-
fill="none" stroke-width="14" stroke-linecap="round"/>
|
|
71
|
-
|
|
72
|
-
<path
|
|
73
|
-
[style.display]="baseAmount > 0 ? '' : 'none'"
|
|
74
|
-
[attr.d]="baseArcPath"
|
|
75
|
-
[attr.stroke]="activeColors.dark"
|
|
76
|
-
fill="none" stroke-width="14" stroke-linecap="round"/>
|
|
77
|
-
|
|
78
|
-
<circle
|
|
79
|
-
[style.display]="baseAmount > 0 ? '' : 'none'"
|
|
80
|
-
[attr.cx]="dotBasePos.x" [attr.cy]="dotBasePos.y"
|
|
81
|
-
r="7" [attr.fill]="activeColors.dark"
|
|
82
|
-
stroke="#ffffff" stroke-width="2.5"
|
|
83
|
-
filter="url(#dotShadowGauge)"/>
|
|
84
|
-
|
|
85
|
-
<circle
|
|
86
|
-
[style.display]="dotTotalVisible ? '' : 'none'"
|
|
87
|
-
[attr.cx]="dotTotalPos.x" [attr.cy]="dotTotalPos.y"
|
|
88
|
-
r="6" [attr.fill]="activeColors.light"
|
|
89
|
-
stroke="#ffffff" stroke-width="2.5"
|
|
90
|
-
filter="url(#dotShadowGauge)"/>
|
|
91
|
-
|
|
92
|
-
<!-- <text
|
|
93
|
-
[attr.x]="limitTextPos.x" [attr.y]="limitTextPos.y"
|
|
94
|
-
font-family="DM Mono,monospace" font-size="9" font-weight="600"
|
|
95
|
-
fill="#8a8fa8" text-anchor="middle">
|
|
96
|
-
{{ currency }}{{ limit }}
|
|
97
|
-
</text> -->
|
|
98
|
-
</svg>
|
|
99
|
-
|
|
100
|
-
<!-- El color del monto cambia a rojo si supera el límite -->
|
|
101
|
-
<div class="gauge-center gauge-center--gauge" [style.bottom.px]="-10 * scale">
|
|
102
|
-
<div class="main-value"
|
|
103
|
-
[style.font-size.px]="26 * scale"
|
|
104
|
-
[style.color]="totalAmount > limit ? activeColors.dark : '#1a1a2e'">
|
|
105
|
-
CPM<br>
|
|
106
|
-
{{ currency }}{{ totalAmount | number:'1.2-2' }}
|
|
107
|
-
</div>
|
|
108
|
-
<div class="limit-label" [style.font-size.px]="16 * scale">
|
|
109
|
-
de {{ currency }}{{ limit | number:'1.2-2' }}
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
<div class="legend">
|
|
115
|
-
<div class="legend-item">
|
|
116
|
-
<span class="dot" [style.background]="activeColors.dark"></span>
|
|
117
|
-
Real
|
|
118
|
-
</div>
|
|
119
|
-
<div class="legend-item">
|
|
120
|
-
<span class="dot"
|
|
121
|
-
[style.background]="activeColors.light"
|
|
122
|
-
[style.border]="'1.5px solid ' + activeColors.dark">
|
|
123
|
-
</span>
|
|
124
|
-
Proyección
|
|
125
|
-
</div>
|
|
126
|
-
<div class="legend-item">
|
|
127
|
-
<span class="dot" style="background:#e8ecf5; border:1px solid #ccc"></span>
|
|
128
|
-
Disponible
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
<!-- Sliders de prueba — solo en devMode -->
|
|
133
|
-
<ng-container *ngIf="devMode">
|
|
134
|
-
<div class="divider"></div>
|
|
135
|
-
<div class="dev-badge">Dev Mode</div>
|
|
136
|
-
<div class="controls">
|
|
137
|
-
|
|
138
|
-
<div class="ctrl-row">
|
|
139
|
-
<div class="ctrl-header">
|
|
140
|
-
<span class="ctrl-label">
|
|
141
|
-
<span class="ctrl-label-dot" [style.background]="activeColors.dark"></span>
|
|
142
|
-
Gasto real (consumido)
|
|
143
|
-
</span>
|
|
144
|
-
<span class="ctrl-val" [style.color]="activeColors.dark">
|
|
145
|
-
{{ currency }}{{ sliderBase | number:'1.2-2' }}
|
|
146
|
-
</span>
|
|
147
|
-
</div>
|
|
148
|
-
<div class="zones-bar">
|
|
149
|
-
<div style="background:#16a34a"></div>
|
|
150
|
-
<div style="background:#ca8a04"></div>
|
|
151
|
-
<div style="background:#ea580c"></div>
|
|
152
|
-
<div style="background:#dc2626"></div>
|
|
153
|
-
</div>
|
|
154
|
-
<input type="range" [min]="0" [max]="sliderMax" step="0.5"
|
|
155
|
-
[value]="sliderBase" (input)="onDevBaseChange($event)">
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
<div class="ctrl-row">
|
|
159
|
-
<div class="ctrl-header">
|
|
160
|
-
<span class="ctrl-label">
|
|
161
|
-
<span class="ctrl-label-dot"
|
|
162
|
-
[style.background]="activeColors.light"
|
|
163
|
-
[style.border]="'1px solid ' + activeColors.dark"
|
|
164
|
-
style="border-radius:50%">
|
|
165
|
-
</span>
|
|
166
|
-
Proyección (presupuesto)
|
|
167
|
-
</span>
|
|
168
|
-
<span class="ctrl-val" [style.color]="activeColors.dark">
|
|
169
|
-
{{ currency }}{{ sliderProjected | number:'1.2-2' }}
|
|
170
|
-
</span>
|
|
171
|
-
</div>
|
|
172
|
-
<div class="zones-bar">
|
|
173
|
-
<div style="background:#16a34a"></div>
|
|
174
|
-
<div style="background:#ca8a04"></div>
|
|
175
|
-
<div style="background:#ea580c"></div>
|
|
176
|
-
<div style="background:#dc2626"></div>
|
|
177
|
-
</div>
|
|
178
|
-
<input type="range" [min]="0" [max]="sliderMax" step="0.5"
|
|
179
|
-
[value]="sliderProjected" (input)="onDevProjectedChange($event)">
|
|
180
|
-
</div>
|
|
181
|
-
|
|
182
|
-
</div>
|
|
183
|
-
</ng-container>
|
|
184
|
-
</ng-container>
|
|
185
|
-
|
|
186
|
-
<ng-template #cpmLegend>
|
|
187
|
-
<div class="legend-tooltip">
|
|
188
|
-
<div class="legend-tooltip__item">
|
|
189
|
-
<span class="dot" [style.background]="activeColors.dark"></span>
|
|
190
|
-
Real<span *ngIf="mode === CpmIndicatorMode.Gauge">: {{ currency }}{{ baseAmount | number:'1.2-2' }}</span>
|
|
191
|
-
</div>
|
|
192
|
-
<div class="legend-tooltip__item">
|
|
193
|
-
<span class="dot" [style.background]="activeColors.light"></span>
|
|
194
|
-
Proyección<span *ngIf="mode === CpmIndicatorMode.Gauge">: {{ currency }}{{ projectedAmount | number:'1.2-2' }}</span>
|
|
195
|
-
</div>
|
|
196
|
-
<div class="legend-tooltip__item">
|
|
197
|
-
<span class="dot" style="background:#e8ecf5"></span>
|
|
198
|
-
Disponible<span *ngIf="mode === CpmIndicatorMode.Gauge">: {{ currency }}{{ (limit - totalAmount) | number:'1.2-2' }}</span>
|
|
199
|
-
</div>
|
|
200
|
-
</div>
|
|
201
|
-
</ng-template>
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
|
|
3
|
-
import { LibeyNgCpmIndicatorComponent } from './libey-ng-cpm-indicator.component';
|
|
4
|
-
|
|
5
|
-
describe('LibeyNgCpmIndicatorComponent', () => {
|
|
6
|
-
let component: LibeyNgCpmIndicatorComponent;
|
|
7
|
-
let fixture: ComponentFixture<LibeyNgCpmIndicatorComponent>;
|
|
8
|
-
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
await TestBed.configureTestingModule({
|
|
11
|
-
declarations: [ LibeyNgCpmIndicatorComponent ]
|
|
12
|
-
})
|
|
13
|
-
.compileComponents();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
fixture = TestBed.createComponent(LibeyNgCpmIndicatorComponent);
|
|
18
|
-
component = fixture.componentInstance;
|
|
19
|
-
fixture.detectChanges();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should create', () => {
|
|
23
|
-
expect(component).toBeTruthy();
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { Component, Input, OnInit, OnChanges, SimpleChanges, Output, EventEmitter, ViewChild, TemplateRef } from '@angular/core';
|
|
2
|
-
import { CpmIndicatorMode,CpmItemType,CpmItem,CpmThresholds,CpmSummary,ZoneColor,SvgPoint,DEFAULT_THRESHOLDS,ZONE_COLORS, CpmColorState,} from '../../utils/CpmIndicator/cpm-indicator-types';
|
|
3
|
-
|
|
4
|
-
@Component({
|
|
5
|
-
selector: 'lib-libey-ng-cpm-indicator',
|
|
6
|
-
templateUrl: './libey-ng-cpm-indicator.component.html',
|
|
7
|
-
styleUrls: ['./libey-ng-cpm-indicator.component.css']
|
|
8
|
-
})
|
|
9
|
-
export class LibeyNgCpmIndicatorComponent implements OnInit, OnChanges {
|
|
10
|
-
|
|
11
|
-
/** Tope máximo del contrato. */
|
|
12
|
-
@Input() limit: number = 250;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Ítems de gasto. El padre es responsable de:
|
|
16
|
-
* - Excluir ítems que no aplican al CPM
|
|
17
|
-
* - Marcar cada uno con CpmItemType.Base o CpmItemType.Projected
|
|
18
|
-
* - Actualizar el array al agregar o eliminar
|
|
19
|
-
*/
|
|
20
|
-
@Input() items: CpmItem[] = [];
|
|
21
|
-
|
|
22
|
-
/** Símbolo de moneda. Por defecto 's/' (sol peruano). */
|
|
23
|
-
@Input() currency: string = 's/';
|
|
24
|
-
|
|
25
|
-
/** Umbrales de zonas como porcentaje del límite (0.0 → 1.0). Usa tercios si no se proporciona. */
|
|
26
|
-
@Input() thresholds?: CpmThresholds;
|
|
27
|
-
|
|
28
|
-
/** Modo de visualización: CPM (solo gauge) o Gauge (montos + leyenda). */
|
|
29
|
-
@Input() mode: CpmIndicatorMode = CpmIndicatorMode.Cpm;
|
|
30
|
-
|
|
31
|
-
/** Activa sliders de prueba. Solo para desarrollo, nunca en producción. */
|
|
32
|
-
@Input() devMode: boolean = false;
|
|
33
|
-
|
|
34
|
-
// ── Outputs ──
|
|
35
|
-
/** Emite el resumen de montos en cada recalculo. Usar para persistir en BD. */
|
|
36
|
-
@Output() cpmChange = new EventEmitter<CpmSummary>();
|
|
37
|
-
|
|
38
|
-
/** Emite true cuando la proyección supera el límite. */
|
|
39
|
-
@Output() limitExceeded = new EventEmitter<boolean>();
|
|
40
|
-
|
|
41
|
-
@ViewChild('cpmLegend') cpmLegend!: TemplateRef<any>;
|
|
42
|
-
|
|
43
|
-
@Output() colorChange = new EventEmitter<CpmColorState>();
|
|
44
|
-
|
|
45
|
-
@Input() scale: number = 1;
|
|
46
|
-
|
|
47
|
-
// ── Propiedades del template ──
|
|
48
|
-
baseAmount: number = 0;
|
|
49
|
-
projectedAmount: number = 0;
|
|
50
|
-
totalAmount: number = 0;
|
|
51
|
-
excess: number = 0;
|
|
52
|
-
activeColors: ZoneColor = ZONE_COLORS['zone1'];
|
|
53
|
-
|
|
54
|
-
trackArcPath: string = '';
|
|
55
|
-
baseArcPath: string = '';
|
|
56
|
-
totalArcPath: string = '';
|
|
57
|
-
dotBasePos: SvgPoint = { x: 0, y: 0 };
|
|
58
|
-
dotTotalPos: SvgPoint = { x: 0, y: 0 };
|
|
59
|
-
dotTotalVisible: boolean = false;
|
|
60
|
-
limitTextPos: SvgPoint = { x: 0, y: 0 };
|
|
61
|
-
|
|
62
|
-
// ── devMode ──
|
|
63
|
-
sliderBase: number = 0;
|
|
64
|
-
sliderProjected: number = 0;
|
|
65
|
-
|
|
66
|
-
get sliderMax(): number {
|
|
67
|
-
return this.limit * 1.5;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Necesario para usar el enum en el template
|
|
71
|
-
readonly CpmIndicatorMode = CpmIndicatorMode;
|
|
72
|
-
|
|
73
|
-
// Geometría del SVG (coordenadas internas del viewBox 220x130)
|
|
74
|
-
private readonly CX = 110;
|
|
75
|
-
private readonly CY = 115;
|
|
76
|
-
private readonly R = 90;
|
|
77
|
-
private readonly ANGLE_START = Math.PI;
|
|
78
|
-
private readonly ANGLE_END = 0;
|
|
79
|
-
|
|
80
|
-
private readonly EXCESS_TOLERANCE = 0.005;
|
|
81
|
-
|
|
82
|
-
constructor() {}
|
|
83
|
-
|
|
84
|
-
ngOnInit(): void {
|
|
85
|
-
this.recalculate();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
ngOnChanges(changes: SimpleChanges): void {
|
|
89
|
-
if (changes['items'] || changes['limit'] || changes['thresholds']) {
|
|
90
|
-
this.recalculate();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
onDevBaseChange(event: Event): void {
|
|
95
|
-
this.sliderBase = parseFloat((event.target as HTMLInputElement).value);
|
|
96
|
-
if (this.sliderBase > this.sliderProjected) {
|
|
97
|
-
this.sliderProjected = this.sliderBase;
|
|
98
|
-
}
|
|
99
|
-
this.recalculateFromSliders();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
onDevProjectedChange(event: Event): void {
|
|
103
|
-
this.sliderProjected = parseFloat((event.target as HTMLInputElement).value);
|
|
104
|
-
if (this.sliderProjected < this.sliderBase) {
|
|
105
|
-
this.sliderProjected = this.sliderBase;
|
|
106
|
-
}
|
|
107
|
-
this.recalculateFromSliders();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Flujo de producción: calcula a partir del array de items
|
|
111
|
-
private recalculate(): void {
|
|
112
|
-
this.baseAmount = this.items
|
|
113
|
-
.filter(i => i.type === CpmItemType.Base)
|
|
114
|
-
.reduce((acc, i) => acc + i.amount, 0);
|
|
115
|
-
|
|
116
|
-
this.projectedAmount = this.items
|
|
117
|
-
.filter(i => i.type === CpmItemType.Projected)
|
|
118
|
-
.reduce((acc, i) => acc + i.amount, 0);
|
|
119
|
-
|
|
120
|
-
this.totalAmount = this.baseAmount + this.projectedAmount;
|
|
121
|
-
|
|
122
|
-
// Sincroniza sliders por si se activa devMode
|
|
123
|
-
this.sliderBase = this.baseAmount;
|
|
124
|
-
this.sliderProjected = this.totalAmount;
|
|
125
|
-
|
|
126
|
-
this.updateGauge();
|
|
127
|
-
this.emitSummary();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Flujo de devMode: calcula a partir de los sliders
|
|
131
|
-
private recalculateFromSliders(): void {
|
|
132
|
-
this.baseAmount = this.sliderBase;
|
|
133
|
-
this.projectedAmount = this.sliderProjected - this.sliderBase;
|
|
134
|
-
this.totalAmount = this.sliderProjected;
|
|
135
|
-
|
|
136
|
-
this.updateGauge();
|
|
137
|
-
this.emitSummary();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private updateGauge(): void {
|
|
141
|
-
const thresholds = this.thresholds ?? DEFAULT_THRESHOLDS;
|
|
142
|
-
this.activeColors = this.getZoneColors(this.totalAmount, thresholds);
|
|
143
|
-
|
|
144
|
-
this.trackArcPath = this.buildArcPath(this.ANGLE_START, this.ANGLE_END);
|
|
145
|
-
this.totalArcPath = this.buildArcPath(this.ANGLE_START, this.valueToAngle(this.totalAmount));
|
|
146
|
-
this.baseArcPath = this.buildArcPath(this.ANGLE_START, this.valueToAngle(this.baseAmount));
|
|
147
|
-
|
|
148
|
-
this.dotBasePos = this.polar(this.valueToAngle(this.baseAmount));
|
|
149
|
-
this.dotTotalPos = this.polar(this.valueToAngle(this.totalAmount));
|
|
150
|
-
|
|
151
|
-
// El punto de proyección solo aparece si va delante del real y no llegó al borde
|
|
152
|
-
this.dotTotalVisible = this.totalAmount > this.baseAmount && this.totalAmount < this.limit;
|
|
153
|
-
|
|
154
|
-
this.limitTextPos = this.polar(this.ANGLE_END, this.R + 20);
|
|
155
|
-
|
|
156
|
-
this.excess = Math.max(0, this.totalAmount - this.limit);
|
|
157
|
-
this.limitExceeded.emit(this.excess > this.EXCESS_TOLERANCE);
|
|
158
|
-
|
|
159
|
-
this.colorChange.emit({
|
|
160
|
-
darkColor: this.activeColors.dark,
|
|
161
|
-
lightColor: this.activeColors.light,
|
|
162
|
-
grayColor: '#E8EAEF',
|
|
163
|
-
limitReached: this.totalAmount >= this.limit,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private emitSummary(): void {
|
|
168
|
-
this.cpmChange.emit({
|
|
169
|
-
baseAmount: this.baseAmount,
|
|
170
|
-
projectedAmount: this.projectedAmount,
|
|
171
|
-
totalAmount: this.totalAmount,
|
|
172
|
-
limit: this.limit,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Clampea el valor al límite para que el arco no desborde visualmente
|
|
177
|
-
private valueToAngle(value: number): number {
|
|
178
|
-
const clamped = Math.min(value, this.limit);
|
|
179
|
-
return Math.PI - (clamped / this.limit) * Math.PI;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
private polar(angle: number, radius: number = this.R): SvgPoint {
|
|
183
|
-
return {
|
|
184
|
-
x: this.CX + radius * Math.cos(angle),
|
|
185
|
-
y: this.CY - radius * Math.sin(angle),
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private buildArcPath(a1: number, a2: number): string {
|
|
190
|
-
const start = this.polar(a1);
|
|
191
|
-
const end = this.polar(a2);
|
|
192
|
-
const large = (a1 - a2) > Math.PI ? 1 : 0;
|
|
193
|
-
return `M ${start.x} ${start.y} A ${this.R} ${this.R} 0 ${large} 1 ${end.x} ${end.y}`;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private getZoneColors(value: number, t: CpmThresholds): ZoneColor {
|
|
197
|
-
const pct = value / this.limit;
|
|
198
|
-
if (pct < t.zone1Max) return ZONE_COLORS['zone1'];
|
|
199
|
-
if (pct < t.zone2Max) return ZONE_COLORS['zone2'];
|
|
200
|
-
if (pct < t.zone3Max) return ZONE_COLORS['zone3'];
|
|
201
|
-
return ZONE_COLORS['zone4'];
|
|
202
|
-
}
|
|
203
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { NgModule } from '@angular/core';
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
3
|
-
import { LibeyNgCpmIndicatorComponent } from './libey-ng-cpm-indicator.component';
|
|
4
|
-
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@NgModule({
|
|
9
|
-
declarations: [
|
|
10
|
-
LibeyNgCpmIndicatorComponent
|
|
11
|
-
],
|
|
12
|
-
imports: [
|
|
13
|
-
CommonModule
|
|
14
|
-
],
|
|
15
|
-
exports: [
|
|
16
|
-
LibeyNgCpmIndicatorComponent
|
|
17
|
-
]
|
|
18
|
-
})
|
|
19
|
-
export class LibeyNgCpmIndicatorModule { }
|