@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
package/dist/ok-drawer.js DELETED
@@ -1,358 +0,0 @@
1
- import { LitElement, css, html } from "lit";
2
- import { property, query } from "lit/decorators.js";
3
- import { define } from "./define.js";
4
- import { o as okIcon, v as iconCloseOutline } from "./shared/icons.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
- close: "Close"
16
- };
17
- const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), ion-button:not([disabled]), ion-input, ion-searchbar, [tabindex]:not([tabindex="-1"])';
18
- class OkDrawer extends LitElement {
19
- constructor() {
20
- super(...arguments);
21
- this.open = false;
22
- this.side = "end";
23
- this.width = "420px";
24
- this.scrim = true;
25
- this.dismissible = true;
26
- this.labels = {};
27
- this.previousFocus = null;
28
- this.hasFooter = false;
29
- this.onDocKeydown = (e) => {
30
- if (!this.open) return;
31
- if (e.key === "Escape") {
32
- if (this.dismissible) {
33
- e.preventDefault();
34
- this.requestClose("esc");
35
- }
36
- return;
37
- }
38
- if (e.key === "Tab") this.trapFocus(e);
39
- };
40
- }
41
- static {
42
- this.styles = css`
43
- :host {
44
- /* Vars overridable (estilo Ionic), default = cadena --ok-* → --ion-* → hex */
45
- --color: var(--ok-text, var(--ion-text-color, #1c1b17));
46
- --color-muted: var(--ok-text-muted, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.6));
47
- --panel-bg: var(--ok-drawer-bg, var(--ok-surface, var(--ion-card-background, var(--ion-background-color, #ffffff))));
48
- --scrim-bg: var(--ok-scrim, rgba(0, 0, 0, 0.4));
49
- --border-soft: var(--ok-border-soft, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.08));
50
- --shadow: var(--ok-shadow-md, 0 10px 40px rgba(0, 0, 0, 0.22));
51
- --font: var(--ok-font, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);
52
-
53
- /* El host no ocupa caja: scrim y panel van a position:fixed sobre todo. */
54
- display: contents;
55
- }
56
-
57
- /* Scrim a pantalla completa (cierra al click si dismissible). */
58
- .scrim {
59
- position: fixed;
60
- inset: 0;
61
- z-index: 2000;
62
- background: var(--scrim-bg);
63
- animation: fade-in 180ms ease;
64
- }
65
-
66
- /* Panel lateral fijo: columna header · body (scroll) · footer. */
67
- .panel {
68
- position: fixed;
69
- top: 0;
70
- bottom: 0;
71
- z-index: 2001;
72
- /* Cadena: token global → prop width (inyectada como var local) → 420px. */
73
- width: var(--ok-drawer-width, var(--drawer-width, 420px));
74
- max-width: 100vw;
75
- display: flex;
76
- flex-direction: column;
77
- box-sizing: border-box;
78
- background: var(--panel-bg);
79
- box-shadow: var(--shadow);
80
- font-family: var(--font);
81
- color: var(--color);
82
- /* Lado por defecto: end (derecha en LTR). */
83
- inset-inline-end: 0;
84
- border-inline-start: 1px solid var(--border-soft);
85
- animation: slide-in-end 200ms ease;
86
- }
87
- :host([side='start']) .panel {
88
- inset-inline-end: auto;
89
- inset-inline-start: 0;
90
- border-inline-start: 0;
91
- border-inline-end: 1px solid var(--border-soft);
92
- animation: slide-in-start 200ms ease;
93
- }
94
-
95
- @keyframes fade-in {
96
- from { opacity: 0; }
97
- to { opacity: 1; }
98
- }
99
- @keyframes slide-in-end {
100
- from { transform: translateX(100%); }
101
- to { transform: translateX(0); }
102
- }
103
- @keyframes slide-in-start {
104
- from { transform: translateX(-100%); }
105
- to { transform: translateX(0); }
106
- }
107
- @media (prefers-reduced-motion: reduce) {
108
- .scrim,
109
- .panel {
110
- animation: none;
111
- }
112
- }
113
-
114
- /* Cabecera: icono + título + acciones extra + botón X. */
115
- .header {
116
- flex: 0 0 auto;
117
- display: flex;
118
- align-items: center;
119
- gap: 0.6rem;
120
- padding: 0.85rem 1rem;
121
- border-bottom: 1px solid var(--border-soft);
122
- }
123
- .header > ion-icon {
124
- flex: 0 0 auto;
125
- font-size: 1.25rem;
126
- color: var(--color-muted);
127
- }
128
- .heading {
129
- flex: 1 1 auto;
130
- min-width: 0;
131
- margin: 0;
132
- font-size: 1.05rem;
133
- font-weight: 600;
134
- overflow: hidden;
135
- text-overflow: ellipsis;
136
- white-space: nowrap;
137
- }
138
- /* Acciones extra de cabecera (slot header-actions) + botón cerrar. */
139
- .header-actions {
140
- flex: 0 0 auto;
141
- display: flex;
142
- align-items: center;
143
- gap: 0.25rem;
144
- }
145
- .close {
146
- flex: 0 0 auto;
147
- display: inline-flex;
148
- align-items: center;
149
- justify-content: center;
150
- width: 32px;
151
- height: 32px;
152
- padding: 0;
153
- border: 0;
154
- border-radius: 8px;
155
- background: none;
156
- color: var(--color-muted);
157
- font-size: 1.25rem;
158
- cursor: pointer;
159
- transition: background-color var(--ok-transition, 150ms ease), color var(--ok-transition, 150ms ease);
160
- }
161
- @media (hover: hover) {
162
- .close:hover {
163
- background: var(--ok-hover, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.06));
164
- color: var(--color);
165
- }
166
- }
167
-
168
- /* Cuerpo: scroll interno. */
169
- .body {
170
- flex: 1 1 auto;
171
- min-height: 0;
172
- overflow-y: auto;
173
- padding: 1rem;
174
- box-sizing: border-box;
175
- }
176
-
177
- /* Pie fijo opcional (se oculta si el slot está vacío). */
178
- .footer {
179
- flex: 0 0 auto;
180
- padding: 0.75rem 1rem;
181
- border-top: 1px solid var(--border-soft);
182
- }
183
- .footer.empty {
184
- display: none;
185
- }
186
-
187
- /* En móvil el panel ocupa todo el ancho. */
188
- @media (max-width: 640px) {
189
- .panel {
190
- width: 100%;
191
- border: 0;
192
- }
193
- }
194
- `;
195
- }
196
- /** Textos efectivos (defaults inglés + overrides). */
197
- get t() {
198
- return { ...DEFAULT_LABELS, ...this.labels };
199
- }
200
- disconnectedCallback() {
201
- super.disconnectedCallback();
202
- document.removeEventListener("keydown", this.onDocKeydown);
203
- }
204
- // ── Apertura / cierre ──────────────────────────────────────────────────────
205
- updated(changed) {
206
- if (!changed.has("open")) return;
207
- const was = changed.get("open");
208
- if (this.open && !was) this.afterOpen();
209
- else if (!this.open && was) this.afterClose();
210
- }
211
- // Al abrir: guarda el foco actual, enfoca el panel y arma el teclado global.
212
- afterOpen() {
213
- this.previousFocus = document.activeElement ?? null;
214
- document.addEventListener("keydown", this.onDocKeydown);
215
- this.updateComplete.then(() => {
216
- const first = this.focusables()[0];
217
- (first ?? this.panel)?.focus();
218
- });
219
- this.dispatchEvent(
220
- new CustomEvent("ok-open", { detail: { open: true }, bubbles: true, composed: true })
221
- );
222
- }
223
- // Al cerrar: desarma el teclado y restaura el foco previo.
224
- afterClose() {
225
- document.removeEventListener("keydown", this.onDocKeydown);
226
- this.previousFocus?.focus?.();
227
- this.previousFocus = null;
228
- }
229
- /**
230
- * Gesto de cierre (scrim/esc/botón): emite `ok-close` CANCELABLE con la razón. Si nadie hace
231
- * `preventDefault()` se auto-cierra (fallback no-controlado); un padre controlado puede vetar.
232
- */
233
- requestClose(reason) {
234
- const ev = new CustomEvent("ok-close", {
235
- detail: { reason },
236
- bubbles: true,
237
- composed: true,
238
- cancelable: true
239
- });
240
- const proceed = this.dispatchEvent(ev);
241
- if (proceed) this.open = false;
242
- }
243
- /** Abre el drawer (azúcar imperativo; equivale a `open = true`). */
244
- show() {
245
- this.open = true;
246
- }
247
- /** Cierra el drawer sin pasar por el gesto cancelable (azúcar imperativo). */
248
- hide() {
249
- this.open = false;
250
- }
251
- // ── Focus trap ─────────────────────────────────────────────────────────────
252
- // Enfocables del panel (shadow) + del contenido slotted (light DOM del host).
253
- focusables() {
254
- const inShadow = this.panel ? Array.from(this.panel.querySelectorAll(FOCUSABLE)) : [];
255
- const inLight = Array.from(this.querySelectorAll(FOCUSABLE));
256
- return [...inShadow, ...inLight].filter((el) => el.offsetParent !== null || el === document.activeElement);
257
- }
258
- // Mantiene Tab/Shift+Tab ciclando dentro del drawer mientras está abierto.
259
- trapFocus(e) {
260
- const list = this.focusables();
261
- if (!list.length) {
262
- e.preventDefault();
263
- this.panel?.focus();
264
- return;
265
- }
266
- const active = this.shadowRoot?.activeElement ?? document.activeElement;
267
- const idx = active ? list.indexOf(active) : -1;
268
- if (e.shiftKey) {
269
- if (idx <= 0) {
270
- e.preventDefault();
271
- list[list.length - 1].focus();
272
- }
273
- } else if (idx === -1 || idx === list.length - 1) {
274
- e.preventDefault();
275
- list[0].focus();
276
- }
277
- }
278
- // ── Interacción ────────────────────────────────────────────────────────────
279
- // Cierra al click en el scrim (solo si dismissible).
280
- onScrimClick() {
281
- if (this.dismissible) this.requestClose("scrim");
282
- }
283
- onFooterSlotChange(e) {
284
- const slot = e.target;
285
- const has = slot.assignedNodes({ flatten: true }).length > 0;
286
- if (has !== this.hasFooter) {
287
- this.hasFooter = has;
288
- this.requestUpdate();
289
- }
290
- }
291
- // ── Render ─────────────────────────────────────────────────────────────────
292
- render() {
293
- if (!this.open) return html``;
294
- return html`
295
- ${this.scrim ? html`<div class="scrim" @click=${() => this.onScrimClick()}></div>` : ""}
296
- <aside
297
- class="panel"
298
- style="--drawer-width:${this.width}"
299
- role="dialog"
300
- aria-modal="true"
301
- aria-label=${this.heading ?? "drawer"}
302
- tabindex="-1"
303
- >
304
- <header class="header">
305
- ${this.icon ? html`<ion-icon .icon=${okIcon(this.icon)} aria-hidden="true"></ion-icon>` : ""}
306
- ${this.heading ? html`<h2 class="heading">${this.heading}</h2>` : html`<span class="heading"></span>`}
307
- <div class="header-actions">
308
- <slot name="header-actions"></slot>
309
- ${this.dismissible ? html`<button
310
- class="close"
311
- type="button"
312
- aria-label=${this.t.close}
313
- title=${this.t.close}
314
- @click=${() => this.requestClose("button")}
315
- >
316
- <ion-icon .icon=${iconCloseOutline} aria-hidden="true"></ion-icon>
317
- </button>` : ""}
318
- </div>
319
- </header>
320
- <div class="body"><slot></slot></div>
321
- <footer class=${this.hasFooter ? "footer" : "footer empty"}>
322
- <slot name="footer" @slotchange=${(e) => this.onFooterSlotChange(e)}></slot>
323
- </footer>
324
- </aside>
325
- `;
326
- }
327
- }
328
- __decorateClass([
329
- property({ type: Boolean, reflect: true })
330
- ], OkDrawer.prototype, "open");
331
- __decorateClass([
332
- property({ reflect: true })
333
- ], OkDrawer.prototype, "side");
334
- __decorateClass([
335
- property()
336
- ], OkDrawer.prototype, "width");
337
- __decorateClass([
338
- property()
339
- ], OkDrawer.prototype, "heading");
340
- __decorateClass([
341
- property()
342
- ], OkDrawer.prototype, "icon");
343
- __decorateClass([
344
- property({ type: Boolean })
345
- ], OkDrawer.prototype, "scrim");
346
- __decorateClass([
347
- property({ type: Boolean })
348
- ], OkDrawer.prototype, "dismissible");
349
- __decorateClass([
350
- property({ attribute: false })
351
- ], OkDrawer.prototype, "labels");
352
- __decorateClass([
353
- query(".panel")
354
- ], OkDrawer.prototype, "panel");
355
- define("ok-drawer", OkDrawer);
356
- export {
357
- OkDrawer
358
- };
@@ -1,280 +0,0 @@
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 OkRangeDual extends LitElement {
14
- constructor() {
15
- super(...arguments);
16
- this.min = 0;
17
- this.max = 100;
18
- this.step = 1;
19
- this.low = 0;
20
- this.high = 100;
21
- this.prefix = "";
22
- this.suffix = "";
23
- this.disabled = false;
24
- }
25
- static {
26
- this.styles = css`
27
- :host {
28
- display: block;
29
- width: 100%;
30
- box-sizing: border-box;
31
- /* Tokens propios estilo Ionic (overridables): --ok-* → --ion-* → hex. */
32
- --track-color: var(--ok-range-track, var(--ion-color-step-150, #e0e2e6));
33
- --fill-color: var(--ok-range-fill, var(--ion-color-primary, #3880ff));
34
- --thumb-bg: var(--ok-range-thumb-bg, var(--ion-background-color, #ffffff));
35
- --thumb-border: var(--ok-range-thumb-border, var(--ion-color-primary, #3880ff));
36
- --label-color: var(--ok-color-medium, var(--ion-color-medium, #92949c));
37
- --value-color: var(--ok-text-color, var(--ion-text-color, #1f2933));
38
- --value-bg: var(--ok-range-value-bg, var(--ion-color-step-100, #f1f2f4));
39
- --focus-ring: var(--ok-range-focus, var(--ion-color-primary-tint, #4c8dff));
40
- }
41
-
42
- .block {
43
- display: grid;
44
- gap: 6px;
45
- width: 100%;
46
- }
47
-
48
- .head {
49
- display: flex;
50
- justify-content: space-between;
51
- align-items: baseline;
52
- font-size: 0.8125rem;
53
- }
54
-
55
- .label {
56
- color: var(--label-color);
57
- font-weight: 500;
58
- }
59
-
60
- .readout {
61
- color: var(--value-color);
62
- font-variant-numeric: tabular-nums;
63
- background: var(--value-bg);
64
- padding: 1px 8px;
65
- border-radius: 6px;
66
- font-size: 12px;
67
- white-space: nowrap;
68
- }
69
-
70
- /* Pista + thumbs apilados */
71
- .range {
72
- position: relative;
73
- height: 24px;
74
- }
75
-
76
- .track {
77
- position: absolute;
78
- top: 50%;
79
- left: 0;
80
- right: 0;
81
- height: 4px;
82
- transform: translateY(-50%);
83
- background: var(--track-color);
84
- border-radius: 999px;
85
- pointer-events: none;
86
- }
87
-
88
- .fill {
89
- position: absolute;
90
- top: 50%;
91
- transform: translateY(-50%);
92
- height: 4px;
93
- border-radius: 999px;
94
- background: var(--fill-color);
95
- pointer-events: none;
96
- }
97
-
98
- /* Ambos inputs ocupan toda la pista; solo el thumb captura el puntero */
99
- input[type='range'] {
100
- position: absolute;
101
- inset: 0;
102
- width: 100%;
103
- height: 100%;
104
- appearance: none;
105
- -webkit-appearance: none;
106
- background: transparent;
107
- pointer-events: none;
108
- margin: 0;
109
- }
110
-
111
- input[type='range']::-webkit-slider-thumb {
112
- -webkit-appearance: none;
113
- appearance: none;
114
- width: 16px;
115
- height: 16px;
116
- border-radius: 50%;
117
- background: var(--thumb-bg);
118
- border: 2px solid var(--thumb-border);
119
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
120
- pointer-events: auto;
121
- cursor: grab;
122
- transition: transform 0.12s ease;
123
- }
124
- input[type='range']::-webkit-slider-thumb:hover {
125
- transform: scale(1.15);
126
- }
127
- input[type='range']:active::-webkit-slider-thumb {
128
- cursor: grabbing;
129
- }
130
-
131
- input[type='range']::-moz-range-thumb {
132
- width: 14px;
133
- height: 14px;
134
- border-radius: 50%;
135
- background: var(--thumb-bg);
136
- border: 2px solid var(--thumb-border);
137
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
138
- pointer-events: auto;
139
- cursor: grab;
140
- }
141
-
142
- input[type='range']::-webkit-slider-runnable-track {
143
- background: transparent;
144
- height: 100%;
145
- }
146
- input[type='range']::-moz-range-track {
147
- background: transparent;
148
- height: 100%;
149
- }
150
-
151
- input[type='range']:focus-visible::-webkit-slider-thumb {
152
- outline: 2px solid var(--focus-ring);
153
- outline-offset: 3px;
154
- }
155
- input[type='range']:focus-visible::-moz-range-thumb {
156
- outline: 2px solid var(--focus-ring);
157
- outline-offset: 3px;
158
- }
159
-
160
- :host([disabled]) {
161
- opacity: 0.55;
162
- pointer-events: none;
163
- }
164
- `;
165
- }
166
- // Clampa low<=high respetando min/max.
167
- clamp() {
168
- const lo = Math.min(Math.max(this.low, this.min), this.max);
169
- const hi = Math.min(Math.max(this.high, this.min), this.max);
170
- return { low: Math.min(lo, hi), high: Math.max(lo, hi) };
171
- }
172
- onLow(e) {
173
- const v = Number(e.target.value);
174
- this.low = Math.min(v, this.high);
175
- this.high = Math.max(this.low, this.high);
176
- this.emit();
177
- }
178
- onHigh(e) {
179
- const v = Number(e.target.value);
180
- this.high = Math.max(v, this.low);
181
- this.low = Math.min(this.low, this.high);
182
- this.emit();
183
- }
184
- emit() {
185
- const { low, high } = this.clamp();
186
- this.dispatchEvent(
187
- new CustomEvent("ok-change", {
188
- detail: { low, high },
189
- bubbles: true,
190
- composed: true
191
- })
192
- );
193
- }
194
- // Formatea un valor con prefijo/sufijo.
195
- fmt(v) {
196
- return `${this.prefix}${v}${this.suffix}`;
197
- }
198
- render() {
199
- const { low, high } = this.clamp();
200
- const span = this.max - this.min || 1;
201
- const leftPct = (low - this.min) / span * 100;
202
- const rightPct = (this.max - high) / span * 100;
203
- return html`
204
- <div class="block">
205
- <div class="head">
206
- ${this.label ? html`<span class="label">${this.label}</span>` : html`<span></span>`}
207
- <span class="readout" aria-live="polite">${this.fmt(low)} – ${this.fmt(high)}</span>
208
- </div>
209
- <div class="range">
210
- <div class="track"></div>
211
- <div
212
- class="fill"
213
- style="left:${leftPct}%; right:${rightPct}%"
214
- ></div>
215
- <input
216
- type="range"
217
- .min=${String(this.min)}
218
- .max=${String(this.max)}
219
- .step=${String(this.step)}
220
- .value=${String(low)}
221
- ?disabled=${this.disabled}
222
- role="slider"
223
- aria-label=${(this.label ? this.label + " " : "") + "mínimo"}
224
- aria-valuemin=${this.min}
225
- aria-valuemax=${this.max}
226
- aria-valuenow=${low}
227
- aria-valuetext=${this.fmt(low)}
228
- @input=${this.onLow}
229
- />
230
- <input
231
- type="range"
232
- .min=${String(this.min)}
233
- .max=${String(this.max)}
234
- .step=${String(this.step)}
235
- .value=${String(high)}
236
- ?disabled=${this.disabled}
237
- role="slider"
238
- aria-label=${(this.label ? this.label + " " : "") + "máximo"}
239
- aria-valuemin=${this.min}
240
- aria-valuemax=${this.max}
241
- aria-valuenow=${high}
242
- aria-valuetext=${this.fmt(high)}
243
- @input=${this.onHigh}
244
- />
245
- </div>
246
- </div>
247
- `;
248
- }
249
- }
250
- __decorateClass([
251
- property()
252
- ], OkRangeDual.prototype, "label");
253
- __decorateClass([
254
- property({ type: Number })
255
- ], OkRangeDual.prototype, "min");
256
- __decorateClass([
257
- property({ type: Number })
258
- ], OkRangeDual.prototype, "max");
259
- __decorateClass([
260
- property({ type: Number })
261
- ], OkRangeDual.prototype, "step");
262
- __decorateClass([
263
- property({ type: Number })
264
- ], OkRangeDual.prototype, "low");
265
- __decorateClass([
266
- property({ type: Number })
267
- ], OkRangeDual.prototype, "high");
268
- __decorateClass([
269
- property()
270
- ], OkRangeDual.prototype, "prefix");
271
- __decorateClass([
272
- property()
273
- ], OkRangeDual.prototype, "suffix");
274
- __decorateClass([
275
- property({ type: Boolean, reflect: true })
276
- ], OkRangeDual.prototype, "disabled");
277
- define("ok-range-dual", OkRangeDual);
278
- export {
279
- OkRangeDual
280
- };