@erplora/outfitkit 0.1.20 → 0.1.21

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