@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,212 @@
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 DEFAULT_LABELS = {
14
+ decrement: "Decrease",
15
+ increment: "Increase"
16
+ };
17
+ class OkQtyStepper extends LitElement {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.value = 0;
21
+ this.min = 0;
22
+ this.step = 1;
23
+ this.disabled = false;
24
+ this.labels = {};
25
+ }
26
+ static {
27
+ this.styles = css`
28
+ :host {
29
+ /* Vars overridable (estilo Ionic), default = cadena --ok-* -> --ion-* -> hex */
30
+ --color: var(--ok-text, var(--ion-text-color, #1c1b17));
31
+ --field-bg: var(--ok-surface, var(--ion-background-color, #ffffff));
32
+ --border-color: var(--ok-border, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.18));
33
+ --border-radius: var(--ok-radius, 8px);
34
+ --field-width: var(--ok-qty-field-width, 3.2rem);
35
+ --font: var(--ok-font, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);
36
+
37
+ /* Inline: ocupa solo lo necesario, alineado con texto circundante. */
38
+ display: inline-flex;
39
+ vertical-align: middle;
40
+ color: var(--color);
41
+ font-family: var(--font);
42
+ }
43
+ :host([disabled]) {
44
+ opacity: 0.5;
45
+ pointer-events: none;
46
+ }
47
+
48
+ .wrap {
49
+ display: inline-flex;
50
+ align-items: stretch;
51
+ border: 1px solid var(--border-color);
52
+ border-radius: var(--border-radius);
53
+ overflow: hidden;
54
+ background: var(--field-bg);
55
+ }
56
+
57
+ /* Botones -/+ : ion-button compactos y sin margen, encajados en la caja. */
58
+ ion-button {
59
+ --padding-start: 0;
60
+ --padding-end: 0;
61
+ --border-radius: 0;
62
+ --box-shadow: none;
63
+ margin: 0;
64
+ height: auto;
65
+ min-width: 2.1rem;
66
+ }
67
+ ion-button ion-icon {
68
+ font-size: 1.1rem;
69
+ }
70
+
71
+ /* Campo central editable: numérico, centrado, sin spinners nativos. */
72
+ .field {
73
+ width: var(--field-width);
74
+ min-width: 0;
75
+ text-align: center;
76
+ border: 0;
77
+ border-left: 1px solid var(--border-color);
78
+ border-right: 1px solid var(--border-color);
79
+ background: transparent;
80
+ color: inherit;
81
+ font: inherit;
82
+ font-size: 0.95rem;
83
+ padding: 0.25rem 0.2rem;
84
+ -moz-appearance: textfield;
85
+ appearance: textfield;
86
+ }
87
+ .field::-webkit-outer-spin-button,
88
+ .field::-webkit-inner-spin-button {
89
+ -webkit-appearance: none;
90
+ margin: 0;
91
+ }
92
+ .field:focus {
93
+ outline: none;
94
+ }
95
+ .field:disabled {
96
+ background: transparent;
97
+ }
98
+ `;
99
+ }
100
+ // Textos efectivos: defaults en inglés + overrides del consumidor.
101
+ get t() {
102
+ return { ...DEFAULT_LABELS, ...this.labels };
103
+ }
104
+ // Recorta `n` al rango [min, max] respetando los límites definidos.
105
+ clamp(n) {
106
+ let v = n;
107
+ if (typeof this.min === "number" && v < this.min) v = this.min;
108
+ if (typeof this.max === "number" && v > this.max) v = this.max;
109
+ return v;
110
+ }
111
+ // Aplica un nuevo valor (con clamp) y emite `ok-change` si cambió.
112
+ commit(next) {
113
+ const clamped = this.clamp(next);
114
+ if (clamped === this.value) {
115
+ this.requestUpdate();
116
+ return;
117
+ }
118
+ this.value = clamped;
119
+ this.dispatchEvent(
120
+ new CustomEvent("ok-change", {
121
+ detail: { value: clamped },
122
+ bubbles: true,
123
+ composed: true
124
+ })
125
+ );
126
+ }
127
+ decrement() {
128
+ if (this.disabled) return;
129
+ this.commit(this.value - this.step);
130
+ }
131
+ increment() {
132
+ if (this.disabled) return;
133
+ this.commit(this.value + this.step);
134
+ }
135
+ // Valida la edición manual: parsea, ignora no-números y hace clamp.
136
+ onInput(e) {
137
+ const raw = e.target.value;
138
+ const parsed = Number(raw);
139
+ if (raw === "" || Number.isNaN(parsed)) return;
140
+ this.commit(parsed);
141
+ }
142
+ // Al salir del campo, normaliza el texto al valor válido actual.
143
+ onBlur(e) {
144
+ const input = e.target;
145
+ const parsed = Number(input.value);
146
+ if (input.value === "" || Number.isNaN(parsed)) {
147
+ input.value = String(this.value);
148
+ } else {
149
+ this.commit(parsed);
150
+ input.value = String(this.value);
151
+ }
152
+ }
153
+ render() {
154
+ const atMin = typeof this.min === "number" && this.value <= this.min;
155
+ const atMax = typeof this.max === "number" && this.value >= this.max;
156
+ return html`<div class="wrap">
157
+ <ion-button
158
+ fill="clear"
159
+ size="small"
160
+ aria-label=${this.t.decrement}
161
+ ?disabled=${this.disabled || atMin}
162
+ @click=${() => this.decrement()}
163
+ >
164
+ <ion-icon slot="icon-only" name="remove"></ion-icon>
165
+ </ion-button>
166
+ <input
167
+ class="field"
168
+ type="number"
169
+ inputmode="numeric"
170
+ .value=${String(this.value)}
171
+ min=${this.min}
172
+ max=${this.max ?? ""}
173
+ step=${this.step}
174
+ ?disabled=${this.disabled}
175
+ @input=${(e) => this.onInput(e)}
176
+ @change=${(e) => this.onBlur(e)}
177
+ @blur=${(e) => this.onBlur(e)}
178
+ />
179
+ <ion-button
180
+ fill="clear"
181
+ size="small"
182
+ aria-label=${this.t.increment}
183
+ ?disabled=${this.disabled || atMax}
184
+ @click=${() => this.increment()}
185
+ >
186
+ <ion-icon slot="icon-only" name="add"></ion-icon>
187
+ </ion-button>
188
+ </div>`;
189
+ }
190
+ }
191
+ __decorateClass([
192
+ property({ type: Number })
193
+ ], OkQtyStepper.prototype, "value");
194
+ __decorateClass([
195
+ property({ type: Number })
196
+ ], OkQtyStepper.prototype, "min");
197
+ __decorateClass([
198
+ property({ type: Number })
199
+ ], OkQtyStepper.prototype, "max");
200
+ __decorateClass([
201
+ property({ type: Number })
202
+ ], OkQtyStepper.prototype, "step");
203
+ __decorateClass([
204
+ property({ type: Boolean, reflect: true })
205
+ ], OkQtyStepper.prototype, "disabled");
206
+ __decorateClass([
207
+ property({ attribute: false })
208
+ ], OkQtyStepper.prototype, "labels");
209
+ define("ok-qty-stepper", OkQtyStepper);
210
+ export {
211
+ OkQtyStepper
212
+ };
@@ -0,0 +1,280 @@
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
+ };
@@ -0,0 +1,199 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { property, state } 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 DEFAULT_LABELS = {
14
+ starLabel: (value, max) => `${value} of ${max}`
15
+ };
16
+ class OkRating extends LitElement {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.value = 0;
20
+ this.max = 5;
21
+ this.readonly = false;
22
+ this.allowHalf = false;
23
+ this.labels = {};
24
+ this.hoverValue = null;
25
+ }
26
+ static {
27
+ this.styles = css`
28
+ :host {
29
+ /* Vars overridable (estilo Ionic), default = cadena --ok-* → --ion-* → hex */
30
+ --color: var(--ok-warning, var(--ion-color-warning, #ffc409));
31
+ --color-empty: var(--ok-border-soft, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.25));
32
+ --size: var(--ok-rating-size, 1.5rem);
33
+ --gap: var(--ok-rating-gap, 0.15rem);
34
+
35
+ display: inline-flex;
36
+ align-items: center;
37
+ gap: var(--gap);
38
+ line-height: 1;
39
+ }
40
+ /* Cada estrella es un botón clicable (o estático en readonly). */
41
+ .star {
42
+ flex: 0 0 auto;
43
+ display: inline-flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ padding: 0;
47
+ margin: 0;
48
+ border: 0;
49
+ background: none;
50
+ color: var(--color-empty);
51
+ cursor: pointer;
52
+ font-size: var(--size);
53
+ transition: background-color var(--ok-transition, 150ms ease),
54
+ color var(--ok-transition, 150ms ease),
55
+ border-color var(--ok-transition, 150ms ease),
56
+ box-shadow var(--ok-transition, 150ms ease), transform 120ms ease;
57
+ }
58
+ .star.filled {
59
+ color: var(--color);
60
+ }
61
+ @media (hover: hover) {
62
+ .star:not(.readonly):hover {
63
+ transform: scale(1.12);
64
+ }
65
+ }
66
+ .star:not(.readonly):active {
67
+ transform: scale(var(--ok-press-scale, 0.97));
68
+ }
69
+ :host([readonly]) .star,
70
+ .star.readonly {
71
+ cursor: default;
72
+ }
73
+ @media (prefers-reduced-motion: reduce) {
74
+ .star:not(.readonly):hover,
75
+ .star:not(.readonly):active {
76
+ transform: none;
77
+ }
78
+ }
79
+ ion-icon {
80
+ pointer-events: none;
81
+ }
82
+ /* Zonas izquierda/derecha para medias estrellas (allow-half). */
83
+ .star.half-mode {
84
+ position: relative;
85
+ }
86
+ .half-zone {
87
+ position: absolute;
88
+ top: 0;
89
+ bottom: 0;
90
+ width: 50%;
91
+ cursor: pointer;
92
+ }
93
+ .half-zone.left {
94
+ left: 0;
95
+ }
96
+ .half-zone.right {
97
+ right: 0;
98
+ }
99
+ `;
100
+ }
101
+ // Textos efectivos: defaults en inglés + overrides del consumidor.
102
+ get t() {
103
+ return { ...DEFAULT_LABELS, ...this.labels };
104
+ }
105
+ // Valor efectivo a pintar: el de hover si lo hay, si no el real.
106
+ get displayValue() {
107
+ return this.hoverValue ?? this.value;
108
+ }
109
+ // Fija el valor y emite `ok-change` (solo si cambia y no es readonly).
110
+ setValue(next) {
111
+ if (this.readonly) return;
112
+ if (next === this.value) return;
113
+ this.value = next;
114
+ this.dispatchEvent(
115
+ new CustomEvent("ok-change", {
116
+ detail: { value: next },
117
+ bubbles: true,
118
+ composed: true
119
+ })
120
+ );
121
+ }
122
+ onEnter(v) {
123
+ if (this.readonly) return;
124
+ this.hoverValue = v;
125
+ }
126
+ onLeave() {
127
+ this.hoverValue = null;
128
+ }
129
+ // Devuelve el icono apropiado para la estrella `index` (1-based) según el valor mostrado.
130
+ iconFor(index) {
131
+ const v = this.displayValue;
132
+ if (v >= index) return "star";
133
+ if (this.allowHalf && v >= index - 0.5) return "star-half";
134
+ return "star-outline";
135
+ }
136
+ // Render de una estrella con medias (zonas izquierda = .5, derecha = entero).
137
+ renderHalfStar(index) {
138
+ const filled = this.displayValue >= index - 0.5;
139
+ return html`<span class=${`star half-mode ${filled ? "filled" : ""}`}>
140
+ <ion-icon .name=${this.iconFor(index)}></ion-icon>
141
+ ${this.readonly ? "" : html`<span
142
+ class="half-zone left"
143
+ @mouseenter=${() => this.onEnter(index - 0.5)}
144
+ @click=${() => this.setValue(index - 0.5)}
145
+ ></span>
146
+ <span
147
+ class="half-zone right"
148
+ @mouseenter=${() => this.onEnter(index)}
149
+ @click=${() => this.setValue(index)}
150
+ ></span>`}
151
+ </span>`;
152
+ }
153
+ // Render de una estrella entera (sin medias).
154
+ renderFullStar(index) {
155
+ const filled = this.displayValue >= index;
156
+ return html`<button
157
+ type="button"
158
+ class=${`star ${filled ? "filled" : ""} ${this.readonly ? "readonly" : ""}`}
159
+ aria-label=${this.t.starLabel(index, this.max)}
160
+ ?disabled=${this.readonly}
161
+ @mouseenter=${() => this.onEnter(index)}
162
+ @click=${() => this.setValue(index)}
163
+ >
164
+ <ion-icon .name=${this.iconFor(index)}></ion-icon>
165
+ </button>`;
166
+ }
167
+ render() {
168
+ const stars = Array.from({ length: Math.max(0, this.max) }, (_, i) => i + 1);
169
+ return html`<span
170
+ role="img"
171
+ aria-label=${this.t.starLabel(this.value, this.max)}
172
+ @mouseleave=${() => this.onLeave()}
173
+ >
174
+ ${stars.map((i) => this.allowHalf ? this.renderHalfStar(i) : this.renderFullStar(i))}
175
+ </span>`;
176
+ }
177
+ }
178
+ __decorateClass([
179
+ property({ type: Number })
180
+ ], OkRating.prototype, "value");
181
+ __decorateClass([
182
+ property({ type: Number })
183
+ ], OkRating.prototype, "max");
184
+ __decorateClass([
185
+ property({ type: Boolean, reflect: true })
186
+ ], OkRating.prototype, "readonly");
187
+ __decorateClass([
188
+ property({ type: Boolean, attribute: "allow-half" })
189
+ ], OkRating.prototype, "allowHalf");
190
+ __decorateClass([
191
+ property({ attribute: false })
192
+ ], OkRating.prototype, "labels");
193
+ __decorateClass([
194
+ state()
195
+ ], OkRating.prototype, "hoverValue");
196
+ define("ok-rating", OkRating);
197
+ export {
198
+ OkRating
199
+ };