@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,736 @@
1
+ import { LitElement, css, nothing, html } from "lit";
2
+ import { property, state } from "lit/decorators.js";
3
+ import { define } from "./define.js";
4
+ import { c as computeAnchor } from "./shared/anchor.js";
5
+ var __defProp = Object.defineProperty;
6
+ var __decorateClass = (decorators, target, key, kind) => {
7
+ var result = void 0;
8
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
9
+ if (decorator = decorators[i])
10
+ result = decorator(target, key, result) || result;
11
+ if (result) __defProp(target, key, result);
12
+ return result;
13
+ };
14
+ const DEFAULT_LABELS = {
15
+ prev: "Mes anterior",
16
+ next: "Mes siguiente",
17
+ open: "Elegir fecha"
18
+ };
19
+ const DEFAULT_PRESETS = [
20
+ { id: "today", label: "Hoy" },
21
+ { id: "7d", label: "7d" },
22
+ { id: "week", label: "Esta semana" },
23
+ { id: "month", label: "Mes" },
24
+ { id: "quarter", label: "Trimestre" },
25
+ { id: "ytd", label: "YTD" }
26
+ ];
27
+ const WEEKDAYS = ["L", "M", "X", "J", "V", "S", "D"];
28
+ class OkDatePicker extends LitElement {
29
+ constructor() {
30
+ super(...arguments);
31
+ this.mode = "single";
32
+ this.value = null;
33
+ this.presets = DEFAULT_PRESETS;
34
+ this.locale = "es-ES";
35
+ this.placeholder = "Seleccionar fecha";
36
+ this.months = 1;
37
+ this.labels = {};
38
+ this.open = false;
39
+ this.side = { end: false, above: false };
40
+ this.viewDate = startOfMonth(/* @__PURE__ */ new Date());
41
+ this.hoverDate = null;
42
+ this.onDocPointer = (e) => {
43
+ if (!this.open) return;
44
+ if (e.composedPath().includes(this)) return;
45
+ this.close();
46
+ };
47
+ this.onKeydown = (e) => {
48
+ if (e.key === "Escape" && this.open) {
49
+ this.close();
50
+ this.fieldEl?.focus();
51
+ }
52
+ };
53
+ }
54
+ static {
55
+ this.styles = css`
56
+ :host {
57
+ /* Tokens propios estilo Ionic (overridables): --ok-* → --ion-* → hex. */
58
+ --bg: var(--ok-surface, var(--ion-card-background, var(--ion-background-color, #ffffff)));
59
+ --field-bg: var(--ok-field-bg, var(--ion-item-background, var(--ion-background-color, #ffffff)));
60
+ --color: var(--ok-text-color, var(--ion-text-color, #1f2933));
61
+ --color-muted: var(--ok-color-medium, var(--ion-color-medium, #92949c));
62
+ --color-faint: var(--ok-color-faint, rgba(var(--ion-text-color-rgb, 31, 41, 51), 0.38));
63
+ --border-color: var(--ok-border, rgba(var(--ion-text-color-rgb, 31, 41, 51), 0.14));
64
+ --hover-bg: var(--ok-hover, rgba(var(--ion-text-color-rgb, 31, 41, 51), 0.06));
65
+ --primary: var(--ok-primary, var(--ion-color-primary, #3880ff));
66
+ --primary-contrast: var(--ok-primary-contrast, var(--ion-color-primary-contrast, #ffffff));
67
+ --primary-soft: var(--ok-primary-soft, rgba(var(--ion-color-primary-rgb, 56, 128, 255), 0.16));
68
+ --primary-strong: var(--ok-primary-shade, var(--ion-color-primary-shade, #3171e0));
69
+ --radius: var(--ok-radius, 12px);
70
+ --radius-sm: var(--ok-radius-sm, 8px);
71
+ --shadow: var(--ok-shadow, 0 10px 32px rgba(0, 0, 0, 0.16));
72
+ --font: var(--ok-font, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);
73
+
74
+ display: block;
75
+ width: 100%;
76
+ position: relative;
77
+ color: var(--color);
78
+ font-family: var(--font);
79
+ }
80
+
81
+ /* Campo disparador: icono de calendario + etiqueta tabular. */
82
+ .field {
83
+ display: inline-flex;
84
+ align-items: center;
85
+ gap: 0.5rem;
86
+ width: 100%;
87
+ box-sizing: border-box;
88
+ padding: 0.5rem 0.75rem;
89
+ min-height: 42px;
90
+ border: 1px solid var(--border-color);
91
+ border-radius: var(--radius-sm);
92
+ background: var(--field-bg);
93
+ color: inherit;
94
+ font: inherit;
95
+ cursor: pointer;
96
+ text-align: left;
97
+ transition: border-color 150ms ease, box-shadow 150ms ease;
98
+ }
99
+ .field:hover {
100
+ border-color: var(--primary);
101
+ }
102
+ .field:focus-visible,
103
+ .field.open {
104
+ outline: none;
105
+ border-color: var(--primary);
106
+ box-shadow: 0 0 0 3px var(--primary-soft);
107
+ }
108
+ .field ion-icon {
109
+ font-size: 1.15rem;
110
+ color: var(--color-muted);
111
+ flex-shrink: 0;
112
+ }
113
+ .field .label {
114
+ flex: 1;
115
+ font-variant-numeric: tabular-nums;
116
+ white-space: nowrap;
117
+ overflow: hidden;
118
+ text-overflow: ellipsis;
119
+ }
120
+ .field .label.placeholder {
121
+ color: var(--color-muted);
122
+ }
123
+
124
+ /* Popover: position:absolute (nunca fixed), anclado al campo vía computeAnchor. */
125
+ .popover {
126
+ position: absolute;
127
+ top: calc(100% + 6px);
128
+ left: 0;
129
+ z-index: 1000;
130
+ background: var(--bg);
131
+ border: 1px solid var(--border-color);
132
+ border-radius: var(--radius);
133
+ box-shadow: var(--shadow);
134
+ padding: 14px;
135
+ user-select: none;
136
+ box-sizing: border-box;
137
+ }
138
+ .popover.end {
139
+ left: auto;
140
+ right: 0;
141
+ }
142
+ .popover.above {
143
+ top: auto;
144
+ bottom: calc(100% + 6px);
145
+ }
146
+
147
+ /* Cuerpo: 1 o 2 meses lado a lado. */
148
+ .months {
149
+ display: grid;
150
+ grid-template-columns: 1fr;
151
+ gap: 22px;
152
+ }
153
+ .months.two {
154
+ grid-template-columns: 1fr 1fr;
155
+ }
156
+ @media (max-width: 620px) {
157
+ .months.two {
158
+ grid-template-columns: 1fr;
159
+ }
160
+ }
161
+ .month {
162
+ width: 280px;
163
+ max-width: 100%;
164
+ }
165
+
166
+ .head {
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: space-between;
170
+ margin-bottom: 12px;
171
+ }
172
+ .title {
173
+ font-size: 0.9rem;
174
+ font-weight: 600;
175
+ color: var(--color);
176
+ text-transform: capitalize;
177
+ }
178
+ .nav {
179
+ display: inline-flex;
180
+ gap: 2px;
181
+ }
182
+ .nav-btn {
183
+ width: 28px;
184
+ height: 28px;
185
+ border-radius: var(--radius-sm);
186
+ background: transparent;
187
+ border: 0;
188
+ color: var(--color-muted);
189
+ display: inline-grid;
190
+ place-items: center;
191
+ cursor: pointer;
192
+ transition: background-color 150ms ease, color 150ms ease;
193
+ }
194
+ .nav-btn:hover {
195
+ background: var(--hover-bg);
196
+ color: var(--color);
197
+ }
198
+ .nav-btn ion-icon {
199
+ font-size: 1.05rem;
200
+ }
201
+
202
+ /* Rejilla de 7 columnas: cabecera de día + días. */
203
+ .grid {
204
+ display: grid;
205
+ grid-template-columns: repeat(7, 1fr);
206
+ gap: 1px;
207
+ }
208
+ .dow {
209
+ font-size: 0.625rem;
210
+ color: var(--color-faint);
211
+ text-align: center;
212
+ padding: 4px 0;
213
+ text-transform: uppercase;
214
+ letter-spacing: 0.06em;
215
+ font-family: var(--ok-font-mono, ui-monospace, 'SF Mono', Menlo, monospace);
216
+ }
217
+
218
+ .day {
219
+ height: 34px;
220
+ border-radius: var(--radius-sm);
221
+ background: transparent;
222
+ border: 0;
223
+ font-size: 0.8125rem;
224
+ color: var(--color);
225
+ display: grid;
226
+ place-items: center;
227
+ cursor: pointer;
228
+ font-variant-numeric: tabular-nums;
229
+ position: relative;
230
+ transition: background-color 120ms ease, color 120ms ease;
231
+ }
232
+ .day:hover:not(:disabled):not(.selected) {
233
+ background: var(--hover-bg);
234
+ }
235
+ .day.muted {
236
+ color: var(--color-faint);
237
+ }
238
+ /* Hoy: anillo interior (inset ring), no relleno. */
239
+ .day.today {
240
+ color: var(--primary);
241
+ font-weight: 600;
242
+ box-shadow: inset 0 0 0 1.5px var(--primary);
243
+ }
244
+ .day.selected {
245
+ background: var(--primary);
246
+ color: var(--primary-contrast);
247
+ font-weight: 600;
248
+ box-shadow: none;
249
+ }
250
+ /* Rango: relleno suave para los días intermedios, esquinas rectas. */
251
+ .day.in-range {
252
+ background: var(--primary-soft);
253
+ color: var(--primary-strong);
254
+ border-radius: 0;
255
+ }
256
+ /* Extremos del rango: píldora con radio asimétrico (abierta hacia el interior). */
257
+ .day.range-start {
258
+ border-radius: var(--radius-sm) 0 0 var(--radius-sm);
259
+ }
260
+ .day.range-end {
261
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
262
+ }
263
+ .day.range-start.range-end {
264
+ border-radius: var(--radius-sm);
265
+ }
266
+ .day:disabled {
267
+ opacity: 0.3;
268
+ cursor: not-allowed;
269
+ }
270
+
271
+ /* Chips de preset rápido. */
272
+ .presets {
273
+ display: flex;
274
+ flex-wrap: wrap;
275
+ gap: 4px;
276
+ margin-top: 12px;
277
+ padding-top: 12px;
278
+ border-top: 1px solid var(--border-color);
279
+ }
280
+ .preset {
281
+ font-size: 0.6875rem;
282
+ padding: 4px 10px;
283
+ background: var(--hover-bg);
284
+ border: 1px solid var(--border-color);
285
+ border-radius: 999px;
286
+ color: var(--color-muted);
287
+ cursor: pointer;
288
+ transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
289
+ }
290
+ .preset:hover {
291
+ color: var(--color);
292
+ border-color: var(--primary);
293
+ }
294
+ .preset.active {
295
+ background: var(--primary-soft);
296
+ color: var(--primary-strong);
297
+ border-color: var(--primary);
298
+ font-weight: 600;
299
+ }
300
+
301
+ @media (prefers-reduced-motion: reduce) {
302
+ .field,
303
+ .nav-btn,
304
+ .day,
305
+ .preset {
306
+ transition: none;
307
+ }
308
+ }
309
+ `;
310
+ }
311
+ get t() {
312
+ return { ...DEFAULT_LABELS, ...this.labels };
313
+ }
314
+ disconnectedCallback() {
315
+ super.disconnectedCallback();
316
+ this.unbind();
317
+ }
318
+ get fieldEl() {
319
+ return this.renderRoot.querySelector(".field");
320
+ }
321
+ // Sincroniza el mes mostrado con el valor cuando se abre (para que el panel arranque donde toca).
322
+ willUpdate(changed) {
323
+ if (changed.has("value") || changed.has("open") && this.open) {
324
+ const anchor = this.anchorDateFromValue();
325
+ if (anchor) this.viewDate = startOfMonth(anchor);
326
+ }
327
+ }
328
+ bind() {
329
+ document.addEventListener("pointerdown", this.onDocPointer, true);
330
+ document.addEventListener("keydown", this.onKeydown);
331
+ }
332
+ unbind() {
333
+ document.removeEventListener("pointerdown", this.onDocPointer, true);
334
+ document.removeEventListener("keydown", this.onKeydown);
335
+ }
336
+ toggle() {
337
+ this.open ? this.close() : this.openPopover();
338
+ }
339
+ openPopover() {
340
+ if (this.open) return;
341
+ this.open = true;
342
+ this.bind();
343
+ this.updateComplete.then(() => {
344
+ const panel = this.renderRoot.querySelector(".popover");
345
+ const field = this.fieldEl;
346
+ if (panel && field) this.side = computeAnchor(field, panel, { gap: 6 });
347
+ });
348
+ }
349
+ close() {
350
+ if (!this.open) return;
351
+ this.open = false;
352
+ this.hoverDate = null;
353
+ this.unbind();
354
+ }
355
+ // ---- Helpers de valor ----------------------------------------------------
356
+ get rangeValue() {
357
+ const v = this.value;
358
+ if (v && typeof v === "object") return v;
359
+ return { start: null, end: null };
360
+ }
361
+ get singleValue() {
362
+ return typeof this.value === "string" ? this.value : null;
363
+ }
364
+ // Fecha de referencia para posicionar el mes inicial.
365
+ anchorDateFromValue() {
366
+ if (this.mode === "range") {
367
+ const r = this.rangeValue;
368
+ return r.start ? parseISO(r.start) : r.end ? parseISO(r.end) : null;
369
+ }
370
+ return this.singleValue ? parseISO(this.singleValue) : null;
371
+ }
372
+ get minDate() {
373
+ return this.min ? parseISO(this.min) : null;
374
+ }
375
+ get maxDate() {
376
+ return this.max ? parseISO(this.max) : null;
377
+ }
378
+ isDisabled(d) {
379
+ const min = this.minDate;
380
+ const max = this.maxDate;
381
+ if (min && d < min) return true;
382
+ if (max && d > max) return true;
383
+ return false;
384
+ }
385
+ // ---- Selección -----------------------------------------------------------
386
+ selectDay(d) {
387
+ if (this.isDisabled(d)) return;
388
+ if (this.mode === "single") {
389
+ this.emit(toISO(d));
390
+ this.close();
391
+ return;
392
+ }
393
+ const r = this.rangeValue;
394
+ if (!r.start || r.start && r.end) {
395
+ this.hoverDate = null;
396
+ this.emit({ start: toISO(d), end: null });
397
+ return;
398
+ }
399
+ const start = parseISO(r.start);
400
+ let next;
401
+ if (d < start) next = { start: toISO(d), end: r.start };
402
+ else next = { start: r.start, end: toISO(d) };
403
+ this.hoverDate = null;
404
+ this.emit(next);
405
+ this.close();
406
+ }
407
+ emit(value) {
408
+ this.value = value;
409
+ this.dispatchEvent(
410
+ new CustomEvent("ok-change", { detail: { value }, bubbles: true, composed: true })
411
+ );
412
+ }
413
+ prevMonth() {
414
+ this.viewDate = addMonths(this.viewDate, -1);
415
+ }
416
+ nextMonth() {
417
+ this.viewDate = addMonths(this.viewDate, 1);
418
+ }
419
+ // ---- Presets -------------------------------------------------------------
420
+ applyPreset(id) {
421
+ const today = startOfDay(/* @__PURE__ */ new Date());
422
+ let value;
423
+ let view = today;
424
+ switch (id) {
425
+ case "today":
426
+ value = this.mode === "range" ? { start: toISO(today), end: toISO(today) } : toISO(today);
427
+ break;
428
+ case "7d": {
429
+ const from = addDays(today, -6);
430
+ value = this.mode === "range" ? { start: toISO(from), end: toISO(today) } : toISO(from);
431
+ view = from;
432
+ break;
433
+ }
434
+ case "week": {
435
+ const from = startOfWeek(today);
436
+ const to = addDays(from, 6);
437
+ value = this.mode === "range" ? { start: toISO(from), end: toISO(to) } : toISO(from);
438
+ view = from;
439
+ break;
440
+ }
441
+ case "month": {
442
+ const from = startOfMonth(today);
443
+ const to = endOfMonth(today);
444
+ value = this.mode === "range" ? { start: toISO(from), end: toISO(to) } : toISO(from);
445
+ view = from;
446
+ break;
447
+ }
448
+ case "quarter": {
449
+ const q = Math.floor(today.getMonth() / 3);
450
+ const from = new Date(today.getFullYear(), q * 3, 1);
451
+ const to = endOfMonth(new Date(today.getFullYear(), q * 3 + 2, 1));
452
+ value = this.mode === "range" ? { start: toISO(from), end: toISO(to) } : toISO(from);
453
+ view = from;
454
+ break;
455
+ }
456
+ case "ytd": {
457
+ const from = new Date(today.getFullYear(), 0, 1);
458
+ value = this.mode === "range" ? { start: toISO(from), end: toISO(today) } : toISO(from);
459
+ view = from;
460
+ break;
461
+ }
462
+ default:
463
+ return;
464
+ }
465
+ this.viewDate = startOfMonth(view);
466
+ this.emit(value);
467
+ if (this.mode === "single") this.close();
468
+ }
469
+ // Decide si un preset está activo comparándolo con el valor actual.
470
+ isPresetActive(id) {
471
+ const expect = this.presetValue(id);
472
+ if (!expect) return false;
473
+ if (this.mode === "range") {
474
+ const r = this.rangeValue;
475
+ const e = expect;
476
+ return r.start === e.start && r.end === e.end;
477
+ }
478
+ return this.singleValue === expect;
479
+ }
480
+ // Calcula (sin emitir) qué valor produciría un preset, para resaltar el chip activo.
481
+ presetValue(id) {
482
+ const today = startOfDay(/* @__PURE__ */ new Date());
483
+ const mk = (from, to) => this.mode === "range" ? { start: toISO(from), end: toISO(to) } : toISO(from);
484
+ switch (id) {
485
+ case "today":
486
+ return mk(today, today);
487
+ case "7d":
488
+ return mk(addDays(today, -6), today);
489
+ case "week": {
490
+ const from = startOfWeek(today);
491
+ return mk(from, addDays(from, 6));
492
+ }
493
+ case "month":
494
+ return mk(startOfMonth(today), endOfMonth(today));
495
+ case "quarter": {
496
+ const q = Math.floor(today.getMonth() / 3);
497
+ return mk(
498
+ new Date(today.getFullYear(), q * 3, 1),
499
+ endOfMonth(new Date(today.getFullYear(), q * 3 + 2, 1))
500
+ );
501
+ }
502
+ case "ytd":
503
+ return mk(new Date(today.getFullYear(), 0, 1), today);
504
+ default:
505
+ return null;
506
+ }
507
+ }
508
+ // ---- Render --------------------------------------------------------------
509
+ // Etiqueta del campo a partir del valor (formato tabular, locale del componente).
510
+ fieldLabel() {
511
+ const fmt = (iso) => new Date(parseISO(iso)).toLocaleDateString(this.locale, {
512
+ day: "2-digit",
513
+ month: "2-digit",
514
+ year: "numeric"
515
+ });
516
+ if (this.mode === "range") {
517
+ const r = this.rangeValue;
518
+ if (r.start && r.end) return `${fmt(r.start)} – ${fmt(r.end)}`;
519
+ if (r.start) return `${fmt(r.start)} – …`;
520
+ return "";
521
+ }
522
+ return this.singleValue ? fmt(this.singleValue) : "";
523
+ }
524
+ render() {
525
+ const label = this.fieldLabel();
526
+ const panels = this.months === 2 ? [0, 1] : [0];
527
+ return html`
528
+ <button
529
+ type="button"
530
+ class="field ${this.open ? "open" : ""}"
531
+ aria-haspopup="dialog"
532
+ aria-expanded=${this.open ? "true" : "false"}
533
+ aria-label=${label || this.t.open}
534
+ @click=${() => this.toggle()}
535
+ >
536
+ <ion-icon name="calendar-outline"></ion-icon>
537
+ <span class="label ${label ? "" : "placeholder"}">${label || this.placeholder}</span>
538
+ </button>
539
+ ${this.open ? html`<div
540
+ class="popover ${this.side.end ? "end" : ""} ${this.side.above ? "above" : ""}"
541
+ role="dialog"
542
+ aria-modal="false"
543
+ >
544
+ <div class="months ${this.months === 2 ? "two" : ""}">
545
+ ${panels.map((offset) => this.renderMonth(addMonths(this.viewDate, offset), offset))}
546
+ </div>
547
+ ${this.presets && this.presets.length ? html`<div class="presets" role="group">
548
+ ${this.presets.map(
549
+ (p) => html`<button
550
+ type="button"
551
+ class="preset ${this.isPresetActive(p.id) ? "active" : ""}"
552
+ aria-pressed=${this.isPresetActive(p.id) ? "true" : "false"}
553
+ @click=${() => this.applyPreset(p.id)}
554
+ >
555
+ ${p.label}
556
+ </button>`
557
+ )}
558
+ </div>` : nothing}
559
+ </div>` : nothing}
560
+ `;
561
+ }
562
+ // Render de un mes: cabecera con navegación (solo el primer panel mueve la vista global), header de
563
+ // días y rejilla de 6 semanas (lunes primero).
564
+ renderMonth(monthDate, offset) {
565
+ const title = monthDate.toLocaleDateString(this.locale, { month: "long", year: "numeric" });
566
+ const days = monthGridDays(monthDate);
567
+ return html`
568
+ <div class="month">
569
+ <div class="head">
570
+ ${offset === 0 ? html`<button
571
+ type="button"
572
+ class="nav-btn"
573
+ aria-label=${this.t.prev}
574
+ @click=${() => this.prevMonth()}
575
+ >
576
+ <ion-icon name="chevron-back-outline"></ion-icon>
577
+ </button>` : html`<span style="width:28px"></span>`}
578
+ <span class="title">${title}</span>
579
+ ${offset === (this.months === 2 ? 1 : 0) ? html`<button
580
+ type="button"
581
+ class="nav-btn"
582
+ aria-label=${this.t.next}
583
+ @click=${() => this.nextMonth()}
584
+ >
585
+ <ion-icon name="chevron-forward-outline"></ion-icon>
586
+ </button>` : html`<span style="width:28px"></span>`}
587
+ </div>
588
+ <div class="grid" role="grid">
589
+ ${WEEKDAYS.map((d) => html`<div class="dow" role="columnheader">${d}</div>`)}
590
+ ${days.map((d) => this.renderDay(d, monthDate))}
591
+ </div>
592
+ </div>
593
+ `;
594
+ }
595
+ renderDay(d, monthDate) {
596
+ const inMonth = d.getMonth() === monthDate.getMonth();
597
+ const disabled = this.isDisabled(d);
598
+ const today = isSameDay(d, /* @__PURE__ */ new Date());
599
+ const cls = ["day"];
600
+ if (!inMonth) cls.push("muted");
601
+ if (today) cls.push("today");
602
+ const flags = this.dayRangeFlags(d);
603
+ if (flags.selected) cls.push("selected");
604
+ if (flags.inRange) cls.push("in-range");
605
+ if (flags.rangeStart) cls.push("range-start");
606
+ if (flags.rangeEnd) cls.push("range-end");
607
+ return html`<button
608
+ type="button"
609
+ class=${cls.join(" ")}
610
+ role="gridcell"
611
+ ?disabled=${disabled}
612
+ aria-selected=${flags.selected ? "true" : "false"}
613
+ aria-current=${today ? "date" : nothing}
614
+ @click=${() => this.selectDay(d)}
615
+ @mouseenter=${() => this.onDayHover(d)}
616
+ >
617
+ ${d.getDate()}
618
+ </button>`;
619
+ }
620
+ // Hover durante la selección del segundo extremo (preview del rango).
621
+ onDayHover(d) {
622
+ if (this.mode !== "range") return;
623
+ const r = this.rangeValue;
624
+ if (r.start && !r.end) this.hoverDate = d;
625
+ }
626
+ // Banderas de estado de un día respecto a la selección (single o range, con preview de hover).
627
+ dayRangeFlags(d) {
628
+ const none = { selected: false, inRange: false, rangeStart: false, rangeEnd: false };
629
+ if (this.mode === "single") {
630
+ const v = this.singleValue;
631
+ return v && isSameDay(d, parseISO(v)) ? { ...none, selected: true } : none;
632
+ }
633
+ const r = this.rangeValue;
634
+ if (!r.start) return none;
635
+ let start = parseISO(r.start);
636
+ let end = r.end ? parseISO(r.end) : this.hoverDate;
637
+ if (!end) {
638
+ return isSameDay(d, start) ? { ...none, selected: true, rangeStart: true, rangeEnd: true } : none;
639
+ }
640
+ if (end < start) [start, end] = [end, start];
641
+ const isStart = isSameDay(d, start);
642
+ const isEnd = isSameDay(d, end);
643
+ if (isStart || isEnd) {
644
+ return {
645
+ selected: true,
646
+ inRange: false,
647
+ rangeStart: isStart,
648
+ rangeEnd: isEnd
649
+ };
650
+ }
651
+ if (d > start && d < end) return { ...none, inRange: true };
652
+ return none;
653
+ }
654
+ }
655
+ __decorateClass([
656
+ property()
657
+ ], OkDatePicker.prototype, "mode");
658
+ __decorateClass([
659
+ property({ attribute: false })
660
+ ], OkDatePicker.prototype, "value");
661
+ __decorateClass([
662
+ property()
663
+ ], OkDatePicker.prototype, "min");
664
+ __decorateClass([
665
+ property()
666
+ ], OkDatePicker.prototype, "max");
667
+ __decorateClass([
668
+ property({ attribute: false })
669
+ ], OkDatePicker.prototype, "presets");
670
+ __decorateClass([
671
+ property()
672
+ ], OkDatePicker.prototype, "locale");
673
+ __decorateClass([
674
+ property()
675
+ ], OkDatePicker.prototype, "placeholder");
676
+ __decorateClass([
677
+ property({ type: Number })
678
+ ], OkDatePicker.prototype, "months");
679
+ __decorateClass([
680
+ property({ attribute: false })
681
+ ], OkDatePicker.prototype, "labels");
682
+ __decorateClass([
683
+ state()
684
+ ], OkDatePicker.prototype, "open");
685
+ __decorateClass([
686
+ state()
687
+ ], OkDatePicker.prototype, "side");
688
+ __decorateClass([
689
+ state()
690
+ ], OkDatePicker.prototype, "viewDate");
691
+ __decorateClass([
692
+ state()
693
+ ], OkDatePicker.prototype, "hoverDate");
694
+ function parseISO(iso) {
695
+ const [y, m, d] = iso.split("-").map(Number);
696
+ return new Date(y, (m || 1) - 1, d || 1);
697
+ }
698
+ function toISO(d) {
699
+ const y = d.getFullYear();
700
+ const m = String(d.getMonth() + 1).padStart(2, "0");
701
+ const day = String(d.getDate()).padStart(2, "0");
702
+ return `${y}-${m}-${day}`;
703
+ }
704
+ function startOfDay(d) {
705
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
706
+ }
707
+ function startOfMonth(d) {
708
+ return new Date(d.getFullYear(), d.getMonth(), 1);
709
+ }
710
+ function endOfMonth(d) {
711
+ return new Date(d.getFullYear(), d.getMonth() + 1, 0);
712
+ }
713
+ function addMonths(d, n) {
714
+ return new Date(d.getFullYear(), d.getMonth() + n, 1);
715
+ }
716
+ function addDays(d, n) {
717
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
718
+ }
719
+ function startOfWeek(d) {
720
+ const day = (d.getDay() + 6) % 7;
721
+ return addDays(startOfDay(d), -day);
722
+ }
723
+ function isSameDay(a, b) {
724
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
725
+ }
726
+ function monthGridDays(monthDate) {
727
+ const first = startOfMonth(monthDate);
728
+ const gridStart = startOfWeek(first);
729
+ const out = [];
730
+ for (let i = 0; i < 42; i++) out.push(addDays(gridStart, i));
731
+ return out;
732
+ }
733
+ define("ok-date-picker", OkDatePicker);
734
+ export {
735
+ OkDatePicker
736
+ };