@erplora/outfitkit 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +457 -0
  3. package/dist/base/anchor.d.ts +13 -0
  4. package/dist/base/define.d.ts +1 -0
  5. package/dist/base/relay.d.ts +1 -0
  6. package/dist/cdn.d.ts +96 -0
  7. package/dist/components/ok-app-launcher/ok-app-launcher.d.ts +57 -0
  8. package/dist/components/ok-audio/ok-audio.d.ts +45 -0
  9. package/dist/components/ok-avatar/ok-avatar.d.ts +36 -0
  10. package/dist/components/ok-avatar-group/ok-avatar-group.d.ts +38 -0
  11. package/dist/components/ok-bar-list/ok-bar-list.d.ts +36 -0
  12. package/dist/components/ok-bento/ok-bento.d.ts +17 -0
  13. package/dist/components/ok-bento-item/ok-bento-item.d.ts +34 -0
  14. package/dist/components/ok-calculator/ok-calculator.d.ts +46 -0
  15. package/dist/components/ok-calendar/ok-calendar.d.ts +63 -0
  16. package/dist/components/ok-carousel/ok-carousel.d.ts +48 -0
  17. package/dist/components/ok-chart/ok-chart.d.ts +55 -0
  18. package/dist/components/ok-chat/ok-chat.d.ts +54 -0
  19. package/dist/components/ok-coachmark/ok-coachmark.d.ts +69 -0
  20. package/dist/components/ok-code/ok-code.d.ts +28 -0
  21. package/dist/components/ok-color-picker/ok-color-picker.d.ts +63 -0
  22. package/dist/components/ok-combo/ok-combo.d.ts +46 -0
  23. package/dist/components/ok-command-palette/ok-command-palette.d.ts +72 -0
  24. package/dist/components/ok-contact-form/ok-contact-form.d.ts +54 -0
  25. package/dist/components/ok-cropper/ok-cropper.d.ts +60 -0
  26. package/dist/components/ok-cta-band/ok-cta-band.d.ts +18 -0
  27. package/dist/components/ok-currency/ok-currency.d.ts +31 -0
  28. package/dist/components/ok-data-table/ok-data-table.d.ts +312 -0
  29. package/dist/components/ok-date-picker/ok-date-picker.d.ts +81 -0
  30. package/dist/components/ok-detail-list/ok-detail-list.d.ts +30 -0
  31. package/dist/components/ok-diff/ok-diff.d.ts +38 -0
  32. package/dist/components/ok-donut/ok-donut.d.ts +38 -0
  33. package/dist/components/ok-drawer/ok-drawer.d.ts +56 -0
  34. package/dist/components/ok-dropzone/ok-dropzone.d.ts +48 -0
  35. package/dist/components/ok-empty-state/ok-empty-state.d.ts +16 -0
  36. package/dist/components/ok-error-page/ok-error-page.d.ts +77 -0
  37. package/dist/components/ok-event-card/ok-event-card.d.ts +56 -0
  38. package/dist/components/ok-feature-card/ok-feature-card.d.ts +23 -0
  39. package/dist/components/ok-file-item/ok-file-item.d.ts +31 -0
  40. package/dist/components/ok-file-manager/ok-file-manager.d.ts +145 -0
  41. package/dist/components/ok-footer/ok-footer.d.ts +10 -0
  42. package/dist/components/ok-funnel/ok-funnel.d.ts +31 -0
  43. package/dist/components/ok-gallery/ok-gallery.d.ts +34 -0
  44. package/dist/components/ok-gauge/ok-gauge.d.ts +49 -0
  45. package/dist/components/ok-heatmap/ok-heatmap.d.ts +45 -0
  46. package/dist/components/ok-hero/ok-hero.d.ts +10 -0
  47. package/dist/components/ok-hover-card/ok-hover-card.d.ts +76 -0
  48. package/dist/components/ok-icon-tile/ok-icon-tile.d.ts +24 -0
  49. package/dist/components/ok-image/ok-image.d.ts +56 -0
  50. package/dist/components/ok-inline-feedback/ok-inline-feedback.d.ts +33 -0
  51. package/dist/components/ok-invoice/ok-invoice.d.ts +137 -0
  52. package/dist/components/ok-json-viewer/ok-json-viewer.d.ts +31 -0
  53. package/dist/components/ok-kanban/ok-kanban.d.ts +56 -0
  54. package/dist/components/ok-kbd/ok-kbd.d.ts +21 -0
  55. package/dist/components/ok-keyboard/ok-keyboard.d.ts +35 -0
  56. package/dist/components/ok-kpi/ok-kpi.d.ts +24 -0
  57. package/dist/components/ok-language-select/ok-language-select.d.ts +31 -0
  58. package/dist/components/ok-lightbox/ok-lightbox.d.ts +59 -0
  59. package/dist/components/ok-logo-cloud/ok-logo-cloud.d.ts +14 -0
  60. package/dist/components/ok-loyalty-card/ok-loyalty-card.d.ts +35 -0
  61. package/dist/components/ok-mail/ok-mail.d.ts +117 -0
  62. package/dist/components/ok-menu/ok-menu.d.ts +75 -0
  63. package/dist/components/ok-menubar/ok-menubar.d.ts +75 -0
  64. package/dist/components/ok-navbar/ok-navbar.d.ts +42 -0
  65. package/dist/components/ok-notification-center/ok-notification-center.d.ts +79 -0
  66. package/dist/components/ok-org-chart/ok-org-chart.d.ts +67 -0
  67. package/dist/components/ok-otp/ok-otp.d.ts +31 -0
  68. package/dist/components/ok-page-header/ok-page-header.d.ts +23 -0
  69. package/dist/components/ok-pagination/ok-pagination.d.ts +44 -0
  70. package/dist/components/ok-pdf/ok-pdf.d.ts +32 -0
  71. package/dist/components/ok-phone/ok-phone.d.ts +48 -0
  72. package/dist/components/ok-pinpad/ok-pinpad.d.ts +29 -0
  73. package/dist/components/ok-pricing-card/ok-pricing-card.d.ts +31 -0
  74. package/dist/components/ok-product-card/ok-product-card.d.ts +25 -0
  75. package/dist/components/ok-qr/ok-qr.d.ts +24 -0
  76. package/dist/components/ok-qty-stepper/ok-qty-stepper.d.ts +35 -0
  77. package/dist/components/ok-range-dual/ok-range-dual.d.ts +38 -0
  78. package/dist/components/ok-rating/ok-rating.d.ts +33 -0
  79. package/dist/components/ok-receipt/ok-receipt.d.ts +103 -0
  80. package/dist/components/ok-reveal/ok-reveal.d.ts +21 -0
  81. package/dist/components/ok-rich-text/ok-rich-text.d.ts +46 -0
  82. package/dist/components/ok-scheduler/ok-scheduler.d.ts +74 -0
  83. package/dist/components/ok-select-card/ok-select-card.d.ts +37 -0
  84. package/dist/components/ok-signature/ok-signature.d.ts +55 -0
  85. package/dist/components/ok-skeleton/ok-skeleton.d.ts +40 -0
  86. package/dist/components/ok-sparkline/ok-sparkline.d.ts +27 -0
  87. package/dist/components/ok-split-button/ok-split-button.d.ts +49 -0
  88. package/dist/components/ok-splitter/ok-splitter.d.ts +36 -0
  89. package/dist/components/ok-stat/ok-stat.d.ts +16 -0
  90. package/dist/components/ok-status-dot/ok-status-dot.d.ts +24 -0
  91. package/dist/components/ok-status-pill/ok-status-pill.d.ts +22 -0
  92. package/dist/components/ok-stepper/ok-stepper.d.ts +33 -0
  93. package/dist/components/ok-store/ok-store.d.ts +33 -0
  94. package/dist/components/ok-tag-input/ok-tag-input.d.ts +39 -0
  95. package/dist/components/ok-testimonial/ok-testimonial.d.ts +21 -0
  96. package/dist/components/ok-time-picker/ok-time-picker.d.ts +50 -0
  97. package/dist/components/ok-timeline/ok-timeline.d.ts +33 -0
  98. package/dist/components/ok-tree/ok-tree.d.ts +46 -0
  99. package/dist/components/ok-video/ok-video.d.ts +49 -0
  100. package/dist/components/ok-widget-board/ok-widget-board.d.ts +71 -0
  101. package/dist/components/ok-wizard/ok-wizard.d.ts +30 -0
  102. package/dist/define.js +8 -0
  103. package/dist/erplora.css +112 -0
  104. package/dist/index.d.ts +158 -0
  105. package/dist/index.js +197 -0
  106. package/dist/layout.css +338 -0
  107. package/dist/ok-app-launcher.js +396 -0
  108. package/dist/ok-audio.js +308 -0
  109. package/dist/ok-avatar-group.js +158 -0
  110. package/dist/ok-avatar.js +179 -0
  111. package/dist/ok-bar-list.js +189 -0
  112. package/dist/ok-bento-item.js +168 -0
  113. package/dist/ok-bento.js +63 -0
  114. package/dist/ok-calculator.js +406 -0
  115. package/dist/ok-calendar.js +541 -0
  116. package/dist/ok-carousel.js +352 -0
  117. package/dist/ok-chart.js +325 -0
  118. package/dist/ok-chat.js +320 -0
  119. package/dist/ok-coachmark.js +500 -0
  120. package/dist/ok-code.js +190 -0
  121. package/dist/ok-color-picker.js +569 -0
  122. package/dist/ok-combo.js +294 -0
  123. package/dist/ok-command-palette.js +448 -0
  124. package/dist/ok-contact-form.js +288 -0
  125. package/dist/ok-cropper.js +404 -0
  126. package/dist/ok-cta-band.js +134 -0
  127. package/dist/ok-currency.js +172 -0
  128. package/dist/ok-data-table.js +1281 -0
  129. package/dist/ok-date-picker.js +736 -0
  130. package/dist/ok-detail-list.js +156 -0
  131. package/dist/ok-diff.js +200 -0
  132. package/dist/ok-donut.js +280 -0
  133. package/dist/ok-drawer.js +357 -0
  134. package/dist/ok-dropzone.js +376 -0
  135. package/dist/ok-empty-state.js +104 -0
  136. package/dist/ok-error-page.js +547 -0
  137. package/dist/ok-event-card.js +384 -0
  138. package/dist/ok-feature-card.js +152 -0
  139. package/dist/ok-file-item.js +259 -0
  140. package/dist/ok-file-manager.js +1116 -0
  141. package/dist/ok-footer.js +67 -0
  142. package/dist/ok-funnel.js +181 -0
  143. package/dist/ok-gallery.js +293 -0
  144. package/dist/ok-gauge.js +385 -0
  145. package/dist/ok-heatmap.js +268 -0
  146. package/dist/ok-hero.js +43 -0
  147. package/dist/ok-hover-card.js +480 -0
  148. package/dist/ok-icon-tile.js +123 -0
  149. package/dist/ok-image.js +471 -0
  150. package/dist/ok-inline-feedback.js +221 -0
  151. package/dist/ok-invoice.js +229 -0
  152. package/dist/ok-json-viewer.js +330 -0
  153. package/dist/ok-kanban.js +427 -0
  154. package/dist/ok-kbd.js +159 -0
  155. package/dist/ok-keyboard.js +402 -0
  156. package/dist/ok-kpi.js +147 -0
  157. package/dist/ok-language-select.js +188 -0
  158. package/dist/ok-lightbox.js +490 -0
  159. package/dist/ok-logo-cloud.js +92 -0
  160. package/dist/ok-loyalty-card.js +353 -0
  161. package/dist/ok-mail.js +562 -0
  162. package/dist/ok-menu.js +529 -0
  163. package/dist/ok-menubar.js +628 -0
  164. package/dist/ok-navbar.js +306 -0
  165. package/dist/ok-notification-center.js +545 -0
  166. package/dist/ok-org-chart.js +619 -0
  167. package/dist/ok-otp.js +199 -0
  168. package/dist/ok-page-header.js +202 -0
  169. package/dist/ok-pagination.js +366 -0
  170. package/dist/ok-pdf.js +160 -0
  171. package/dist/ok-phone.js +225 -0
  172. package/dist/ok-pinpad.js +171 -0
  173. package/dist/ok-pricing-card.js +184 -0
  174. package/dist/ok-product-card.js +178 -0
  175. package/dist/ok-qr.js +652 -0
  176. package/dist/ok-qty-stepper.js +212 -0
  177. package/dist/ok-range-dual.js +280 -0
  178. package/dist/ok-rating.js +199 -0
  179. package/dist/ok-receipt.js +183 -0
  180. package/dist/ok-reveal.js +94 -0
  181. package/dist/ok-rich-text.js +538 -0
  182. package/dist/ok-scheduler.js +518 -0
  183. package/dist/ok-select-card.js +231 -0
  184. package/dist/ok-signature.js +267 -0
  185. package/dist/ok-skeleton.js +345 -0
  186. package/dist/ok-sparkline.js +150 -0
  187. package/dist/ok-split-button.js +251 -0
  188. package/dist/ok-splitter.js +289 -0
  189. package/dist/ok-stat.js +77 -0
  190. package/dist/ok-status-dot.js +163 -0
  191. package/dist/ok-status-pill.js +123 -0
  192. package/dist/ok-stepper.js +299 -0
  193. package/dist/ok-store.js +83 -0
  194. package/dist/ok-tag-input.js +358 -0
  195. package/dist/ok-testimonial.js +136 -0
  196. package/dist/ok-time-picker.js +472 -0
  197. package/dist/ok-timeline.js +251 -0
  198. package/dist/ok-tree.js +266 -0
  199. package/dist/ok-video.js +362 -0
  200. package/dist/ok-widget-board.js +265 -0
  201. package/dist/ok-wizard.js +153 -0
  202. package/dist/outfitkit.js +96 -0
  203. package/dist/shared/anchor.js +14 -0
  204. package/dist/store/controller.d.ts +17 -0
  205. package/dist/store/idb.d.ts +16 -0
  206. package/dist/store/store.d.ts +39 -0
  207. package/dist/store-controller.js +31 -0
  208. package/dist/store.js +182 -0
  209. package/dist/theme.example.css +70 -0
  210. package/package.json +147 -0
@@ -0,0 +1,385 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { property } from "lit/decorators.js";
3
+ import { define } from "./define.js";
4
+ var __defProp = Object.defineProperty;
5
+ var __decorateClass = (decorators, target, key, kind) => {
6
+ var result = void 0;
7
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
+ if (decorator = decorators[i])
9
+ result = decorator(target, key, result) || result;
10
+ if (result) __defProp(target, key, result);
11
+ return result;
12
+ };
13
+ class OkGauge extends LitElement {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.type = "arc";
17
+ this.value = 0;
18
+ this.min = 0;
19
+ this.max = 100;
20
+ this.thresholds = [];
21
+ this.color = "";
22
+ this.size = 140;
23
+ this.unit = "%";
24
+ }
25
+ static {
26
+ this.styles = css`
27
+ :host {
28
+ display: block;
29
+ width: 100%;
30
+ box-sizing: border-box;
31
+
32
+ /* Tokens propios estilo Ionic (overridables): --ok-* → --ion-* → hex. */
33
+ --bg: var(--ok-surface, var(--ion-background-color, #ffffff));
34
+ --line: var(--ok-border-color, var(--ion-border-color, #e0e0e0));
35
+ --track: var(--ok-track-color, var(--ion-color-light, #f4f5f8));
36
+ --ink: var(--ok-text-color, var(--ion-text-color, #1f2933));
37
+ --ink-muted: var(--ok-color-medium, var(--ion-color-medium, #92949c));
38
+ --accent: var(--ok-primary, var(--ion-color-primary, #3880ff));
39
+ }
40
+
41
+ .gauge {
42
+ display: inline-flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ gap: 6px;
46
+ box-sizing: border-box;
47
+ }
48
+
49
+ /* Arc y ring centran su SVG; bullet ocupa el ancho del contenedor. */
50
+ .gauge--arc,
51
+ .gauge--ring {
52
+ width: 100%;
53
+ align-items: center;
54
+ }
55
+ .gauge--bullet {
56
+ display: flex;
57
+ width: 100%;
58
+ align-items: stretch;
59
+ gap: 4px;
60
+ }
61
+
62
+ .dial {
63
+ position: relative;
64
+ display: inline-flex;
65
+ align-items: flex-end;
66
+ justify-content: center;
67
+ }
68
+
69
+ svg {
70
+ display: block;
71
+ overflow: visible;
72
+ }
73
+
74
+ /* Trazo de pista y relleno (compartidos arc/ring). */
75
+ .track {
76
+ fill: none;
77
+ stroke: var(--track);
78
+ stroke-linecap: round;
79
+ }
80
+ .fill {
81
+ fill: none;
82
+ stroke-linecap: round;
83
+ transition: stroke-dashoffset 0.6s cubic-bezier(0.16, 1, 0.3, 1);
84
+ }
85
+
86
+ /* Valor central. */
87
+ .value {
88
+ position: absolute;
89
+ font-weight: 600;
90
+ letter-spacing: -0.03em;
91
+ color: var(--ink);
92
+ font-variant-numeric: tabular-nums;
93
+ line-height: 1;
94
+ }
95
+ .value sup {
96
+ font-weight: 500;
97
+ color: var(--ink-muted);
98
+ margin-left: 1px;
99
+ }
100
+ /* Arc: valor abajo dentro del semicírculo. */
101
+ .gauge--arc .value {
102
+ left: 0;
103
+ right: 0;
104
+ bottom: 2px;
105
+ text-align: center;
106
+ }
107
+ /* Ring: valor centrado. */
108
+ .gauge--ring .dial {
109
+ align-items: center;
110
+ }
111
+ .gauge--ring .value {
112
+ inset: 0;
113
+ display: grid;
114
+ place-items: center;
115
+ }
116
+
117
+ .label {
118
+ font-size: 0.6875rem;
119
+ color: var(--ink-muted);
120
+ text-transform: uppercase;
121
+ letter-spacing: 0.06em;
122
+ text-align: center;
123
+ }
124
+ .sublabel {
125
+ font-size: 0.8125rem;
126
+ color: var(--ink-muted);
127
+ text-align: center;
128
+ }
129
+
130
+ /* ---- Bullet (barra Tufte) ---- */
131
+ .bullet {
132
+ display: flex;
133
+ flex-direction: column;
134
+ gap: 6px;
135
+ width: 100%;
136
+ }
137
+ .bullet__head {
138
+ display: flex;
139
+ justify-content: space-between;
140
+ align-items: baseline;
141
+ gap: 8px;
142
+ font-size: 0.8125rem;
143
+ color: var(--ink-muted);
144
+ }
145
+ .bullet__head b {
146
+ color: var(--ink);
147
+ font-weight: 600;
148
+ font-variant-numeric: tabular-nums;
149
+ }
150
+ .bullet__bar {
151
+ position: relative;
152
+ height: 14px;
153
+ background: var(--track);
154
+ border-radius: 999px;
155
+ overflow: hidden;
156
+ }
157
+ .bullet__zone {
158
+ position: absolute;
159
+ top: 0;
160
+ bottom: 0;
161
+ }
162
+ .bullet__fill {
163
+ position: absolute;
164
+ top: 3px;
165
+ bottom: 3px;
166
+ left: 3px;
167
+ border-radius: 999px;
168
+ transition: width 0.4s cubic-bezier(0.16, 1, 0.3, 1);
169
+ }
170
+ .bullet__target {
171
+ position: absolute;
172
+ top: -2px;
173
+ bottom: -2px;
174
+ width: 2px;
175
+ background: var(--ink);
176
+ border-radius: 1px;
177
+ transform: translateX(-1px);
178
+ }
179
+ `;
180
+ }
181
+ // Fracción normalizada del valor en [0,1] respecto a min..max.
182
+ get fraction() {
183
+ const range = this.max - this.min || 1;
184
+ const f = (this.value - this.min) / range;
185
+ return Math.min(1, Math.max(0, f));
186
+ }
187
+ // Color efectivo del relleno: prop explícita > primer threshold cuyo `to` >= value > accent.
188
+ get fillColor() {
189
+ if (this.color) return this.color;
190
+ if (this.thresholds?.length) {
191
+ const sorted = [...this.thresholds].sort((a, b) => a.to - b.to);
192
+ for (const z of sorted) {
193
+ if (this.value <= z.to) return z.color;
194
+ }
195
+ return sorted[sorted.length - 1].color;
196
+ }
197
+ return "var(--accent)";
198
+ }
199
+ // Texto del valor con unidad como superíndice si la hay.
200
+ // Se envuelve en un único <span> para que el valor y la unidad fluyan inline
201
+ // como un solo bloque; en el ring el contenedor es grid/place-items y, sin el
202
+ // wrapper, el número y el <sup> caerían en filas distintas (uno encima de otro).
203
+ renderValueText() {
204
+ const n = Number.isInteger(this.value) ? this.value : Number(this.value.toFixed(1));
205
+ return html`<span class="value-text"
206
+ >${n}${this.unit ? html`<sup>${this.unit}</sup>` : null}</span
207
+ >`;
208
+ }
209
+ // ---- ARC: semicírculo 180° ----
210
+ renderArc() {
211
+ const w = this.size;
212
+ const stroke = Math.max(8, Math.round(w * 0.085));
213
+ const pad = stroke / 2 + 2;
214
+ const r = (w - pad * 2) / 2;
215
+ const cx = w / 2;
216
+ const cy = w / 2;
217
+ const h = r + pad;
218
+ const x0 = cx - r;
219
+ const x1 = cx + r;
220
+ const arcPath = `M ${x0} ${cy} A ${r} ${r} 0 0 1 ${x1} ${cy}`;
221
+ const len = Math.PI * r;
222
+ const offset = len * (1 - this.fraction);
223
+ const valueFs = Math.round(w * 0.18);
224
+ return html`
225
+ <div class="dial" style=${`width:${w}px;height:${h}px;`}>
226
+ <svg
227
+ width=${w}
228
+ height=${h}
229
+ viewBox=${`0 0 ${w} ${h}`}
230
+ role="img"
231
+ aria-label=${this.ariaText()}
232
+ >
233
+ <path class="track" d=${arcPath} stroke-width=${stroke} />
234
+ <path
235
+ class="fill"
236
+ d=${arcPath}
237
+ stroke=${this.fillColor}
238
+ stroke-width=${stroke}
239
+ stroke-dasharray=${len}
240
+ stroke-dashoffset=${offset}
241
+ />
242
+ </svg>
243
+ <div class="value" style=${`font-size:${valueFs}px;`}>${this.renderValueText()}</div>
244
+ </div>
245
+ ${this.label ? html`<div class="label">${this.label}</div>` : null}
246
+ ${this.sublabel ? html`<div class="sublabel">${this.sublabel}</div>` : null}
247
+ `;
248
+ }
249
+ // ---- RING: círculo completo determinado, empieza a las 12h ----
250
+ renderRing() {
251
+ const w = this.size;
252
+ const stroke = Math.max(6, Math.round(w * 0.075));
253
+ const pad = stroke / 2 + 2;
254
+ const r = (w - pad * 2) / 2;
255
+ const cx = w / 2;
256
+ const cy = w / 2;
257
+ const circ = 2 * Math.PI * r;
258
+ const offset = circ * (1 - this.fraction);
259
+ const valueFs = Math.round(w * 0.2);
260
+ return html`
261
+ <div class="dial" style=${`width:${w}px;height:${w}px;`}>
262
+ <svg
263
+ width=${w}
264
+ height=${w}
265
+ viewBox=${`0 0 ${w} ${w}`}
266
+ role="img"
267
+ aria-label=${this.ariaText()}
268
+ style="transform: rotate(-90deg);"
269
+ >
270
+ <circle class="track" cx=${cx} cy=${cy} r=${r} stroke-width=${stroke} />
271
+ <circle
272
+ class="fill"
273
+ cx=${cx}
274
+ cy=${cy}
275
+ r=${r}
276
+ stroke=${this.fillColor}
277
+ stroke-width=${stroke}
278
+ stroke-dasharray=${circ}
279
+ stroke-dashoffset=${offset}
280
+ />
281
+ </svg>
282
+ <div class="value" style=${`font-size:${valueFs}px;`}>${this.renderValueText()}</div>
283
+ </div>
284
+ ${this.label ? html`<div class="label">${this.label}</div>` : null}
285
+ ${this.sublabel ? html`<div class="sublabel">${this.sublabel}</div>` : null}
286
+ `;
287
+ }
288
+ // ---- BULLET: barra Tufte horizontal con zonas + target ----
289
+ renderBullet() {
290
+ const range = this.max - this.min || 1;
291
+ const pct = (v) => Math.min(100, Math.max(0, (v - this.min) / range * 100));
292
+ const sorted = this.thresholds?.length ? [...this.thresholds].sort((a, b) => a.to - b.to) : [];
293
+ let prev = this.min;
294
+ const zones = sorted.map((z) => {
295
+ const left = pct(prev);
296
+ const width = pct(z.to) - left;
297
+ prev = z.to;
298
+ return { left, width, color: z.color };
299
+ });
300
+ const fillW = pct(this.value);
301
+ const valueNum = Number.isInteger(this.value) ? this.value : Number(this.value.toFixed(1));
302
+ return html`
303
+ <div class="bullet" role="img" aria-label=${this.ariaText()}>
304
+ ${this.label || this.sublabel ? html`
305
+ <div class="bullet__head">
306
+ <span>${this.label ?? ""}</span>
307
+ <b>${valueNum}${this.unit}${this.sublabel ? html` · ${this.sublabel}` : ""}</b>
308
+ </div>
309
+ ` : null}
310
+ <div class="bullet__bar">
311
+ ${zones.map(
312
+ (z) => html`
313
+ <span
314
+ class="bullet__zone"
315
+ style=${`left:${z.left}%;width:${z.width}%;background:${this.zoneTint(
316
+ z.color
317
+ )};`}
318
+ ></span>
319
+ `
320
+ )}
321
+ <span
322
+ class="bullet__fill"
323
+ style=${`width:calc(${fillW}% - 6px);background:${this.fillColor};`}
324
+ ></span>
325
+ ${this.target != null ? html`<span class="bullet__target" style=${`left:${pct(this.target)}%;`}></span>` : null}
326
+ </div>
327
+ </div>
328
+ `;
329
+ }
330
+ // Tinte suave para el fondo de zona (la librería antigua usaba color-mix al ~18%).
331
+ zoneTint(color) {
332
+ return `color-mix(in srgb, ${color} 18%, transparent)`;
333
+ }
334
+ // Texto accesible que describe el gauge.
335
+ ariaText() {
336
+ const base = `${this.label ? this.label + ": " : ""}${this.value}${this.unit} de ${this.max}${this.unit}`;
337
+ return this.target != null ? `${base} (objetivo ${this.target}${this.unit})` : base;
338
+ }
339
+ render() {
340
+ if (this.type === "bullet") {
341
+ return html`<div class="gauge gauge--bullet">${this.renderBullet()}</div>`;
342
+ }
343
+ if (this.type === "ring") {
344
+ return html`<div class="gauge gauge--ring">${this.renderRing()}</div>`;
345
+ }
346
+ return html`<div class="gauge gauge--arc">${this.renderArc()}</div>`;
347
+ }
348
+ }
349
+ __decorateClass([
350
+ property()
351
+ ], OkGauge.prototype, "type");
352
+ __decorateClass([
353
+ property({ type: Number })
354
+ ], OkGauge.prototype, "value");
355
+ __decorateClass([
356
+ property({ type: Number })
357
+ ], OkGauge.prototype, "min");
358
+ __decorateClass([
359
+ property({ type: Number })
360
+ ], OkGauge.prototype, "max");
361
+ __decorateClass([
362
+ property({ attribute: false })
363
+ ], OkGauge.prototype, "thresholds");
364
+ __decorateClass([
365
+ property({ type: Number })
366
+ ], OkGauge.prototype, "target");
367
+ __decorateClass([
368
+ property()
369
+ ], OkGauge.prototype, "label");
370
+ __decorateClass([
371
+ property()
372
+ ], OkGauge.prototype, "sublabel");
373
+ __decorateClass([
374
+ property()
375
+ ], OkGauge.prototype, "color");
376
+ __decorateClass([
377
+ property({ type: Number })
378
+ ], OkGauge.prototype, "size");
379
+ __decorateClass([
380
+ property()
381
+ ], OkGauge.prototype, "unit");
382
+ define("ok-gauge", OkGauge);
383
+ export {
384
+ OkGauge
385
+ };
@@ -0,0 +1,268 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { property } from "lit/decorators.js";
3
+ import { define } from "./define.js";
4
+ var __defProp = Object.defineProperty;
5
+ var __decorateClass = (decorators, target, key, kind) => {
6
+ var result = void 0;
7
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
+ if (decorator = decorators[i])
9
+ result = decorator(target, key, result) || result;
10
+ if (result) __defProp(target, key, result);
11
+ return result;
12
+ };
13
+ const MONTHS_ES = [
14
+ "Ene",
15
+ "Feb",
16
+ "Mar",
17
+ "Abr",
18
+ "May",
19
+ "Jun",
20
+ "Jul",
21
+ "Ago",
22
+ "Sep",
23
+ "Oct",
24
+ "Nov",
25
+ "Dic"
26
+ ];
27
+ class OkHeatmap extends LitElement {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.data = [];
31
+ this.levels = 5;
32
+ this.scale = "";
33
+ this.layout = "weeks";
34
+ this.cellSize = 12;
35
+ this.legend = false;
36
+ }
37
+ static {
38
+ this.styles = css`
39
+ :host {
40
+ display: block;
41
+ width: 100%;
42
+ box-sizing: border-box;
43
+
44
+ /* Tokens propios estilo Ionic: --ok-* → --ion-* → hex. */
45
+ --brand: var(--ok-primary, var(--ion-color-primary, #3880ff));
46
+ --empty: var(--ok-surface-shade, var(--ion-color-light, #eceef1));
47
+ --muted: var(--ok-color-medium, var(--ion-color-medium, #92949c));
48
+ --cell: 12px;
49
+ --radius: 2px;
50
+ --gap: 3px;
51
+ }
52
+
53
+ .wrap {
54
+ display: flex;
55
+ flex-direction: column;
56
+ gap: 10px;
57
+ width: 100%;
58
+ box-sizing: border-box;
59
+ }
60
+
61
+ /* ---- Layout 'weeks': 7 filas, flujo por columnas (una columna por semana) ---- */
62
+ .heat {
63
+ display: grid;
64
+ grid-auto-flow: column;
65
+ grid-template-rows: repeat(7, 1fr);
66
+ gap: var(--gap);
67
+ /* Permite scroll horizontal cuando hay muchas semanas en móvil. */
68
+ overflow-x: auto;
69
+ padding-bottom: 2px;
70
+ }
71
+
72
+ /* ---- Layout 'year': 12 columnas de meses, cada una un mini-grid de días ---- */
73
+ .year {
74
+ display: grid;
75
+ grid-template-columns: repeat(12, 1fr);
76
+ gap: 8px;
77
+ }
78
+ .month {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 4px;
82
+ min-width: 0;
83
+ }
84
+ .month-label {
85
+ font-size: 10px;
86
+ text-transform: uppercase;
87
+ letter-spacing: 0.04em;
88
+ color: var(--muted);
89
+ text-align: center;
90
+ }
91
+ .days {
92
+ display: grid;
93
+ grid-template-columns: repeat(7, 1fr);
94
+ gap: 2px;
95
+ }
96
+
97
+ /* ---- Celda ---- */
98
+ .cell {
99
+ width: var(--cell);
100
+ height: var(--cell);
101
+ border-radius: var(--radius);
102
+ background: var(--empty);
103
+ }
104
+ .year .cell {
105
+ width: 100%;
106
+ aspect-ratio: 1 / 1;
107
+ height: auto;
108
+ border-radius: 1px;
109
+ }
110
+ /* Intensidades: mezcla brand X% con el fondo vacío (escala progresiva). */
111
+ .cell[data-v='1'] { background: color-mix(in srgb, var(--brand) 25%, var(--empty)); }
112
+ .cell[data-v='2'] { background: color-mix(in srgb, var(--brand) 50%, var(--empty)); }
113
+ .cell[data-v='3'] { background: color-mix(in srgb, var(--brand) 75%, var(--empty)); }
114
+ .cell[data-v='4'] { background: var(--brand); }
115
+
116
+ /* ---- Leyenda Menos → Más ---- */
117
+ .legend {
118
+ display: flex;
119
+ justify-content: flex-end;
120
+ align-items: center;
121
+ gap: 5px;
122
+ font-size: 10.5px;
123
+ color: var(--muted);
124
+ }
125
+ .legend .cell {
126
+ width: var(--cell);
127
+ height: var(--cell);
128
+ }
129
+ `;
130
+ }
131
+ // Tier máximo pintable (los tiers se clampean a [0, maxTier]).
132
+ get maxTier() {
133
+ return Math.max(1, this.levels - 1);
134
+ }
135
+ // Aplica el color de escala explícito como override del token de host.
136
+ hostStyle() {
137
+ const parts = [`--cell:${this.cellSize}px`];
138
+ if (this.scale) parts.push(`--brand:${this.scale}`);
139
+ return parts.join(";");
140
+ }
141
+ // Calcula umbrales por cuantiles sobre los valores presentes (reparte tiers 1..maxTier).
142
+ thresholds() {
143
+ const vals = (this.data ?? []).map((d) => d.value).filter((v) => typeof v === "number" && v > 0).sort((a, b) => a - b);
144
+ if (!vals.length) return [];
145
+ const max = this.maxTier;
146
+ const th = [];
147
+ for (let k = 1; k <= max; k++) {
148
+ const idx = Math.min(vals.length - 1, Math.floor(k / (max + 1) * vals.length));
149
+ th.push(vals[idx]);
150
+ }
151
+ return th;
152
+ }
153
+ // Deriva el tier de intensidad de una celda (level explícito, o value vía cuantiles).
154
+ tierOf(cell, th) {
155
+ if (typeof cell.level === "number") {
156
+ return Math.max(0, Math.min(this.maxTier, Math.round(cell.level)));
157
+ }
158
+ const v = cell.value ?? 0;
159
+ if (v <= 0 || !th.length) return 0;
160
+ let tier = 1;
161
+ for (let k = 0; k < th.length; k++) {
162
+ if (v >= th[k]) tier = k + 1;
163
+ }
164
+ return Math.min(this.maxTier, tier);
165
+ }
166
+ // Texto del tooltip: label explícito, o fecha/clave + valor.
167
+ titleOf(cell) {
168
+ if (cell.label) return cell.label;
169
+ const head = cell.date ?? cell.key ?? "";
170
+ const tail = typeof cell.value === "number" ? `${cell.value}` : "";
171
+ if (head && tail) return `${head} · ${tail}`;
172
+ return head || tail || "";
173
+ }
174
+ // Resuelve cada celda de entrada (tier + title) preservando el orden.
175
+ resolve() {
176
+ const th = this.thresholds();
177
+ return (this.data ?? []).map((cell) => ({
178
+ tier: this.tierOf(cell, th),
179
+ title: this.titleOf(cell)
180
+ }));
181
+ }
182
+ // Pinta una celda (con data-v si tiene intensidad).
183
+ renderCell(c) {
184
+ return html`<div
185
+ class="cell"
186
+ data-v=${c.tier > 0 ? String(c.tier) : ""}
187
+ role="gridcell"
188
+ title=${c.title || ""}
189
+ ></div>`;
190
+ }
191
+ // Layout 'weeks': el grid se rellena por columnas automáticamente (grid-auto-flow: column).
192
+ renderWeeks(cells) {
193
+ return html`<div class="heat" role="grid" aria-label="Heatmap de actividad">
194
+ ${cells.map((c) => this.renderCell(c))}
195
+ </div>`;
196
+ }
197
+ // Layout 'year': agrupa por mes (de la fecha) y pinta 12 columnas de mini-grids.
198
+ renderYear() {
199
+ const th = this.thresholds();
200
+ const buckets = Array.from(
201
+ { length: 12 },
202
+ () => []
203
+ );
204
+ for (const raw of this.data ?? []) {
205
+ const resolved = { tier: this.tierOf(raw, th), title: this.titleOf(raw) };
206
+ let month = 0;
207
+ let day = 0;
208
+ if (raw.date) {
209
+ const d = new Date(raw.date);
210
+ if (!Number.isNaN(d.getTime())) {
211
+ month = d.getMonth();
212
+ day = d.getDate();
213
+ }
214
+ }
215
+ buckets[month].push({ day, cell: resolved });
216
+ }
217
+ return html`<div class="year" role="grid" aria-label="Heatmap anual">
218
+ ${buckets.map(
219
+ (items, m) => html`<div class="month" role="row" aria-label=${MONTHS_ES[m]}>
220
+ <div class="month-label">${MONTHS_ES[m]}</div>
221
+ <div class="days">
222
+ ${items.slice().sort((a, b) => a.day - b.day).map((it) => this.renderCell(it.cell))}
223
+ </div>
224
+ </div>`
225
+ )}
226
+ </div>`;
227
+ }
228
+ // Leyenda Menos → [vacío, 1, 2, …, maxTier] → Más.
229
+ renderLegend() {
230
+ const tiers = Array.from({ length: this.maxTier + 1 }, (_, i) => i);
231
+ return html`<div class="legend" aria-hidden="true">
232
+ <span>Menos</span>
233
+ ${tiers.map(
234
+ (t) => html`<div class="cell" data-v=${t > 0 ? String(t) : ""}></div>`
235
+ )}
236
+ <span>Más</span>
237
+ </div>`;
238
+ }
239
+ render() {
240
+ const cells = this.layout === "year" ? [] : this.resolve();
241
+ return html`<div class="wrap" style=${this.hostStyle()}>
242
+ ${this.layout === "year" ? this.renderYear() : this.renderWeeks(cells)}
243
+ ${this.legend ? this.renderLegend() : null}
244
+ </div>`;
245
+ }
246
+ }
247
+ __decorateClass([
248
+ property({ attribute: false })
249
+ ], OkHeatmap.prototype, "data");
250
+ __decorateClass([
251
+ property({ type: Number })
252
+ ], OkHeatmap.prototype, "levels");
253
+ __decorateClass([
254
+ property()
255
+ ], OkHeatmap.prototype, "scale");
256
+ __decorateClass([
257
+ property()
258
+ ], OkHeatmap.prototype, "layout");
259
+ __decorateClass([
260
+ property({ type: Number, attribute: "cell-size" })
261
+ ], OkHeatmap.prototype, "cellSize");
262
+ __decorateClass([
263
+ property({ type: Boolean })
264
+ ], OkHeatmap.prototype, "legend");
265
+ define("ok-heatmap", OkHeatmap);
266
+ export {
267
+ OkHeatmap
268
+ };
@@ -0,0 +1,43 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { define } from "./define.js";
3
+ class OkHero extends LitElement {
4
+ static {
5
+ this.styles = css`
6
+ :host {
7
+ --color: var(--ok-text, var(--ion-text-color, #1c1b17));
8
+ --color-muted: var(--ok-muted, rgba(var(--ion-text-color-rgb, 24, 24, 27), 0.55));
9
+ --max-width: var(--ok-container-max, 1140px);
10
+ --padding: var(--ok-spacing, var(--ion-padding, 16px));
11
+ --font: var(--ok-font, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);
12
+
13
+ display: block;
14
+ color: var(--color);
15
+ font-family: var(--font);
16
+ }
17
+ .h {
18
+ max-width: var(--max-width);
19
+ margin-inline: auto;
20
+ padding: clamp(2rem, 6vw, 5rem) var(--padding);
21
+ display: grid;
22
+ gap: 1rem;
23
+ }
24
+ ::slotted([slot='title']) { font-size: clamp(1.8rem, 4vw, 3rem); line-height: 1.1; margin: 0; }
25
+ ::slotted([slot='subtitle']) { font-size: clamp(1rem, 2vw, 1.25rem); color: var(--color-muted); margin: 0; max-width: 42ch; }
26
+ .actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-top: 0.5rem; }
27
+ `;
28
+ }
29
+ render() {
30
+ return html`
31
+ <section class="h">
32
+ <slot name="title"></slot>
33
+ <slot name="subtitle"></slot>
34
+ <div class="actions"><slot name="actions"></slot></div>
35
+ <slot></slot>
36
+ </section>
37
+ `;
38
+ }
39
+ }
40
+ define("ok-hero", OkHero);
41
+ export {
42
+ OkHero
43
+ };