@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,500 @@
1
+ import { LitElement, css, render, nothing, 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
+ prev: "Back",
15
+ next: "Next",
16
+ finish: "Done",
17
+ skip: "Skip",
18
+ step: "Step {n} of {total}"
19
+ };
20
+ class OkCoachmark extends LitElement {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.steps = [];
24
+ this.current = 0;
25
+ this.open = false;
26
+ this.labels = {};
27
+ this.rect = null;
28
+ this.bubble = null;
29
+ this.portalRoot = null;
30
+ this.onReflow = () => {
31
+ if (this.open) this.measure();
32
+ };
33
+ this.onKeydown = (e) => {
34
+ if (!this.open) return;
35
+ if (e.key === "Escape") {
36
+ this.skip();
37
+ } else if (e.key === "ArrowRight") {
38
+ this.next();
39
+ } else if (e.key === "ArrowLeft") {
40
+ this.prev();
41
+ }
42
+ };
43
+ }
44
+ static {
45
+ this.styles = css`
46
+ :host {
47
+ /* Tokens propios estilo Ionic (overridables): --ok-* → --ion-* → hex. */
48
+ --brand: var(--ok-primary, var(--ion-color-primary, #3880ff));
49
+ --brand-soft: var(--ok-primary-soft, rgba(var(--ion-color-primary-rgb, 56, 128, 255), 0.25));
50
+ --brand-contrast: var(--ok-primary-contrast, var(--ion-color-primary-contrast, #ffffff));
51
+ --panel-bg: var(--ok-surface, var(--ion-card-background, var(--ion-background-color, #ffffff)));
52
+ --color: var(--ok-text, var(--ion-text-color, #1c1b17));
53
+ --color-muted: var(--ok-text-muted, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.62));
54
+ --border-radius: var(--ok-radius, 14px);
55
+ --shadow: var(--ok-shadow, 0 16px 48px rgba(0, 0, 0, 0.22));
56
+ --scrim: var(--ok-coach-scrim, rgba(0, 0, 0, 0.45));
57
+ --dot-off: var(--ok-coach-dot, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.2));
58
+ --font: var(--ok-font, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);
59
+
60
+ display: block;
61
+ width: 100%;
62
+ }
63
+
64
+ /* El host no pinta nada visible: todo el overlay se porta a document.body. */
65
+ :host {
66
+ position: absolute;
67
+ width: 0;
68
+ height: 0;
69
+ overflow: hidden;
70
+ }
71
+
72
+ /* ---- Estilos del portal (adoptados por el shadow del portal en body) ---- */
73
+
74
+ /* Scrim de cuatro paneles que rodean el hueco del spotlight (deja ver el target). */
75
+ .scrim-piece {
76
+ position: fixed;
77
+ z-index: 9000;
78
+ background: var(--scrim);
79
+ transition: opacity 200ms ease;
80
+ }
81
+
82
+ /* Anillo de spotlight pulsante alrededor del target. */
83
+ .spot {
84
+ position: fixed;
85
+ z-index: 9001;
86
+ border-radius: var(--ok-coach-spot-radius, 10px);
87
+ pointer-events: none;
88
+ box-shadow: 0 0 0 3px var(--brand), 0 0 0 7px var(--brand-soft);
89
+ animation: ok-coach-pulse 2s ease-in-out infinite;
90
+ }
91
+ @keyframes ok-coach-pulse {
92
+ 0%,
93
+ 100% {
94
+ box-shadow: 0 0 0 3px var(--brand), 0 0 0 7px var(--brand-soft);
95
+ }
96
+ 50% {
97
+ box-shadow: 0 0 0 3px var(--brand), 0 0 0 12px transparent;
98
+ }
99
+ }
100
+ @media (prefers-reduced-motion: reduce) {
101
+ .spot {
102
+ animation: none;
103
+ }
104
+ }
105
+
106
+ /* Bubble anclado al target. position:fixed porque sigue rects reales del viewport. */
107
+ .bubble {
108
+ position: fixed;
109
+ z-index: 9002;
110
+ box-sizing: border-box;
111
+ width: 290px;
112
+ max-width: calc(100vw - 24px);
113
+ padding: 14px 16px 12px;
114
+ background: var(--panel-bg);
115
+ color: var(--color);
116
+ border: 1px solid var(--brand);
117
+ border-radius: var(--border-radius);
118
+ box-shadow: var(--shadow);
119
+ font-family: var(--font);
120
+ }
121
+
122
+ /* Flecha del bubble: cuadrado rotado pegado al borde según el lado. */
123
+ .arrow {
124
+ position: absolute;
125
+ width: 12px;
126
+ height: 12px;
127
+ background: var(--panel-bg);
128
+ border: 1px solid var(--brand);
129
+ transform: rotate(45deg);
130
+ }
131
+ .arrow.bottom {
132
+ top: -7px;
133
+ left: 50%;
134
+ margin-left: -6px;
135
+ border-right: 0;
136
+ border-bottom: 0;
137
+ }
138
+ .arrow.top {
139
+ bottom: -7px;
140
+ left: 50%;
141
+ margin-left: -6px;
142
+ border-left: 0;
143
+ border-top: 0;
144
+ }
145
+ .arrow.right {
146
+ left: -7px;
147
+ top: 50%;
148
+ margin-top: -6px;
149
+ border-right: 0;
150
+ border-top: 0;
151
+ }
152
+ .arrow.left {
153
+ right: -7px;
154
+ top: 50%;
155
+ margin-top: -6px;
156
+ border-left: 0;
157
+ border-bottom: 0;
158
+ }
159
+
160
+ .step-label {
161
+ font-size: 10px;
162
+ font-weight: 600;
163
+ text-transform: uppercase;
164
+ letter-spacing: 0.06em;
165
+ color: var(--brand);
166
+ margin: 0 0 4px;
167
+ }
168
+ .title {
169
+ font-size: 14px;
170
+ font-weight: 600;
171
+ color: var(--color);
172
+ margin: 0 0 4px;
173
+ }
174
+ .text {
175
+ font-size: 12.5px;
176
+ line-height: 1.5;
177
+ color: var(--color-muted);
178
+ margin: 0 0 12px;
179
+ }
180
+
181
+ .bar {
182
+ display: flex;
183
+ align-items: center;
184
+ justify-content: space-between;
185
+ gap: 10px;
186
+ }
187
+ .dots {
188
+ display: inline-flex;
189
+ gap: 4px;
190
+ flex-wrap: wrap;
191
+ }
192
+ .dots button {
193
+ width: 6px;
194
+ height: 6px;
195
+ padding: 0;
196
+ border: 0;
197
+ border-radius: 50%;
198
+ background: var(--dot-off);
199
+ cursor: pointer;
200
+ transition: width 180ms ease, background-color 180ms ease, border-radius 180ms ease;
201
+ }
202
+ .dots button.on {
203
+ width: 18px;
204
+ border-radius: 3px;
205
+ background: var(--brand);
206
+ }
207
+
208
+ .actions {
209
+ display: inline-flex;
210
+ align-items: center;
211
+ gap: 6px;
212
+ }
213
+ /* Botones propios mínimos (ghost + primary). No reusamos ion-button para no acoplar el portal a
214
+ estilos de host; son controles simples accesibles. */
215
+ .btn {
216
+ font: inherit;
217
+ font-size: 12px;
218
+ font-weight: 600;
219
+ line-height: 1;
220
+ padding: 7px 11px;
221
+ border-radius: 8px;
222
+ border: 1px solid transparent;
223
+ cursor: pointer;
224
+ background: none;
225
+ color: var(--color-muted);
226
+ transition: background-color 150ms ease, color 150ms ease, opacity 150ms ease;
227
+ }
228
+ .btn.ghost:hover {
229
+ background: rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.06);
230
+ }
231
+ .btn.primary {
232
+ background: var(--brand);
233
+ color: var(--brand-contrast);
234
+ border-color: var(--brand);
235
+ }
236
+ .btn.primary:hover {
237
+ opacity: 0.92;
238
+ }
239
+ .btn:focus-visible {
240
+ outline: 2px solid var(--brand);
241
+ outline-offset: 2px;
242
+ }
243
+ @media (prefers-reduced-motion: reduce) {
244
+ .scrim-piece,
245
+ .dots button,
246
+ .btn {
247
+ transition: none;
248
+ }
249
+ }
250
+ `;
251
+ }
252
+ // Textos efectivos: defaults en inglés + overrides del consumidor.
253
+ get t() {
254
+ return { ...DEFAULT_LABELS, ...this.labels };
255
+ }
256
+ get step() {
257
+ return this.steps[this.current];
258
+ }
259
+ get isLast() {
260
+ return this.current >= this.steps.length - 1;
261
+ }
262
+ disconnectedCallback() {
263
+ super.disconnectedCallback();
264
+ this.unbind();
265
+ const host = this.portalRoot?.host;
266
+ this.portalRoot = null;
267
+ if (host && host.parentNode) host.parentNode.removeChild(host);
268
+ }
269
+ updated(changed) {
270
+ if (changed.has("open")) {
271
+ if (this.open) {
272
+ this.bind();
273
+ this.measure();
274
+ } else {
275
+ this.unbind();
276
+ }
277
+ } else if (this.open && (changed.has("current") || changed.has("steps"))) {
278
+ this.measure();
279
+ }
280
+ if (!this.open && !this.portalRoot) return;
281
+ render(this.open ? this.overlayTemplate() : nothing, this.ensurePortal());
282
+ }
283
+ bind() {
284
+ document.addEventListener("keydown", this.onKeydown);
285
+ window.addEventListener("scroll", this.onReflow, true);
286
+ window.addEventListener("resize", this.onReflow);
287
+ }
288
+ unbind() {
289
+ document.removeEventListener("keydown", this.onKeydown);
290
+ window.removeEventListener("scroll", this.onReflow, true);
291
+ window.removeEventListener("resize", this.onReflow);
292
+ }
293
+ // Crea (una vez) el portal: un div en document.body con shadow propio que ADOPTA la hoja de
294
+ // estilos del componente, de modo que scrim/spot/bubble se ven idénticos fuera del host.
295
+ ensurePortal() {
296
+ if (this.portalRoot) return this.portalRoot;
297
+ const host = document.createElement("div");
298
+ host.setAttribute("data-ok-coachmark-portal", "");
299
+ document.body.appendChild(host);
300
+ const root = host.attachShadow({ mode: "open" });
301
+ const styles = this.constructor.elementStyles;
302
+ root.adoptedStyleSheets = styles.map((s) => s instanceof CSSStyleSheet ? s : s.styleSheet).filter((s) => !!s);
303
+ this.portalRoot = root;
304
+ return root;
305
+ }
306
+ // Localiza el target del paso actual, mide su rect y calcula la posición del bubble.
307
+ measure() {
308
+ const sel = this.step?.target;
309
+ let el = null;
310
+ if (sel) {
311
+ try {
312
+ el = document.querySelector(sel);
313
+ } catch {
314
+ el = null;
315
+ }
316
+ }
317
+ this.rect = el ? el.getBoundingClientRect() : null;
318
+ this.bubble = this.computeBubble(this.rect);
319
+ }
320
+ // Coloca el bubble respecto al rect del target eligiendo el lado con sitio (edge-aware). Mismo
321
+ // criterio que computeAnchor: lado preferido y, si no cabe, volteo al opuesto; clamp a márgenes.
322
+ computeBubble(rect) {
323
+ const vw = window.innerWidth;
324
+ const vh = window.innerHeight;
325
+ const gap = 14;
326
+ const margin = 8;
327
+ const bw = Math.min(290, vw - 24);
328
+ const bh = 150;
329
+ if (!rect) {
330
+ return { top: (vh - bh) / 2, left: (vw - bw) / 2, side: "bottom" };
331
+ }
332
+ const want = this.step?.placement ?? "bottom";
333
+ const fits = {
334
+ bottom: rect.bottom + gap + bh <= vh - margin,
335
+ top: rect.top - gap - bh >= margin,
336
+ right: rect.right + gap + bw <= vw - margin,
337
+ left: rect.left - gap - bw >= margin
338
+ };
339
+ const opposite = {
340
+ bottom: "top",
341
+ top: "bottom",
342
+ left: "right",
343
+ right: "left"
344
+ };
345
+ const order = [want, opposite[want], "bottom", "top", "right", "left"];
346
+ const side = order.find((s) => fits[s]) ?? want;
347
+ let top;
348
+ let left;
349
+ if (side === "bottom" || side === "top") {
350
+ left = rect.left + rect.width / 2 - bw / 2;
351
+ top = side === "bottom" ? rect.bottom + gap : rect.top - gap - bh;
352
+ } else {
353
+ top = rect.top + rect.height / 2 - bh / 2;
354
+ left = side === "right" ? rect.right + gap : rect.left - gap - bw;
355
+ }
356
+ left = Math.max(margin, Math.min(left, vw - bw - margin));
357
+ top = Math.max(margin, Math.min(top, vh - bh - margin));
358
+ return { top, left, side };
359
+ }
360
+ // ---- Navegación / eventos ----
361
+ emit(name, detail) {
362
+ this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
363
+ }
364
+ goTo(index) {
365
+ if (index < 0 || index >= this.steps.length || index === this.current) return;
366
+ this.current = index;
367
+ this.emit("ok-step", { index });
368
+ }
369
+ next() {
370
+ if (this.isLast) {
371
+ this.finish();
372
+ return;
373
+ }
374
+ const index = this.current + 1;
375
+ this.current = index;
376
+ this.emit("ok-next", { index });
377
+ this.emit("ok-step", { index });
378
+ }
379
+ prev() {
380
+ if (this.current <= 0) return;
381
+ const index = this.current - 1;
382
+ this.current = index;
383
+ this.emit("ok-prev", { index });
384
+ this.emit("ok-step", { index });
385
+ }
386
+ finish() {
387
+ this.open = false;
388
+ this.emit("ok-finish");
389
+ }
390
+ skip() {
391
+ this.open = false;
392
+ this.emit("ok-skip");
393
+ }
394
+ // El host no renderiza nada visible (es un controlador); el overlay vive en el portal.
395
+ render() {
396
+ return nothing;
397
+ }
398
+ // Cuatro paneles de scrim que rodean el hueco del target (efecto recorte sin clip-path), o un
399
+ // scrim completo si no hay target.
400
+ scrimTemplate() {
401
+ const r = this.rect;
402
+ if (!r) {
403
+ return html`<div class="scrim-piece" style="inset:0" @click=${() => this.skip()}></div>`;
404
+ }
405
+ const pad = 6;
406
+ const t = Math.max(0, r.top - pad);
407
+ const b = Math.max(0, r.bottom + pad);
408
+ const l = Math.max(0, r.left - pad);
409
+ const rr = Math.max(0, r.right + pad);
410
+ const click = () => this.skip();
411
+ return html`
412
+ <div class="scrim-piece" style="left:0;top:0;right:0;height:${t}px" @click=${click}></div>
413
+ <div class="scrim-piece" style="left:0;top:${b}px;right:0;bottom:0" @click=${click}></div>
414
+ <div class="scrim-piece" style="left:0;top:${t}px;width:${l}px;height:${b - t}px" @click=${click}></div>
415
+ <div class="scrim-piece" style="left:${rr}px;top:${t}px;right:0;height:${b - t}px" @click=${click}></div>
416
+ `;
417
+ }
418
+ spotTemplate() {
419
+ const r = this.rect;
420
+ if (!r) return nothing;
421
+ const pad = 6;
422
+ return html`<div
423
+ class="spot"
424
+ style="left:${r.left - pad}px;top:${r.top - pad}px;width:${r.width + pad * 2}px;height:${r.height + pad * 2}px"
425
+ ></div>`;
426
+ }
427
+ bubbleTemplate() {
428
+ const pos = this.bubble;
429
+ const s = this.step;
430
+ if (!pos || !s) return nothing;
431
+ const total = this.steps.length;
432
+ const label = this.t.step.replace("{n}", String(this.current + 1)).replace("{total}", String(total));
433
+ return html`
434
+ <div
435
+ class="bubble"
436
+ role="dialog"
437
+ aria-modal="true"
438
+ aria-label=${s.title ?? label}
439
+ style="top:${pos.top}px;left:${pos.left}px"
440
+ >
441
+ <span class="arrow ${pos.side}"></span>
442
+ <p class="step-label">${label}</p>
443
+ ${s.title ? html`<h2 class="title">${s.title}</h2>` : nothing}
444
+ ${s.text ? html`<p class="text">${s.text}</p>` : nothing}
445
+ <div class="bar">
446
+ <div class="dots" role="tablist">
447
+ ${this.steps.map(
448
+ (_, i) => html`<button
449
+ type="button"
450
+ role="tab"
451
+ class=${i === this.current ? "on" : ""}
452
+ aria-selected=${i === this.current ? "true" : "false"}
453
+ aria-label=${this.t.step.replace("{n}", String(i + 1)).replace("{total}", String(total))}
454
+ @click=${() => this.goTo(i)}
455
+ ></button>`
456
+ )}
457
+ </div>
458
+ <div class="actions">
459
+ <button type="button" class="btn ghost" @click=${() => this.skip()}>
460
+ ${this.t.skip}
461
+ </button>
462
+ ${this.current > 0 ? html`<button type="button" class="btn ghost" @click=${() => this.prev()}>
463
+ ${this.t.prev}
464
+ </button>` : nothing}
465
+ <button type="button" class="btn primary" @click=${() => this.next()}>
466
+ ${this.isLast ? this.t.finish : this.t.next}
467
+ </button>
468
+ </div>
469
+ </div>
470
+ </div>
471
+ `;
472
+ }
473
+ // Overlay completo renderizado en el portal de document.body.
474
+ overlayTemplate() {
475
+ if (!this.step) return nothing;
476
+ return html`${this.scrimTemplate()}${this.spotTemplate()}${this.bubbleTemplate()}`;
477
+ }
478
+ }
479
+ __decorateClass([
480
+ property({ attribute: false })
481
+ ], OkCoachmark.prototype, "steps");
482
+ __decorateClass([
483
+ property({ type: Number })
484
+ ], OkCoachmark.prototype, "current");
485
+ __decorateClass([
486
+ property({ type: Boolean })
487
+ ], OkCoachmark.prototype, "open");
488
+ __decorateClass([
489
+ property({ attribute: false })
490
+ ], OkCoachmark.prototype, "labels");
491
+ __decorateClass([
492
+ state()
493
+ ], OkCoachmark.prototype, "rect");
494
+ __decorateClass([
495
+ state()
496
+ ], OkCoachmark.prototype, "bubble");
497
+ define("ok-coachmark", OkCoachmark);
498
+ export {
499
+ OkCoachmark
500
+ };
@@ -0,0 +1,190 @@
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
+ class OkCode extends LitElement {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.code = "";
17
+ this.inline = false;
18
+ this.language = "";
19
+ this.copy = false;
20
+ this.copied = false;
21
+ }
22
+ static {
23
+ this.styles = css`
24
+ :host {
25
+ display: block;
26
+ width: 100%;
27
+ /* Tokens propios estilo Ionic (overridables): --ok-* → --ion-* → hex. */
28
+ --bg: var(--ok-surface, var(--ion-background-color, #f5f6f8));
29
+ --inline-bg: var(--ok-surface-3, var(--ion-color-light, #e6e8ec));
30
+ --border-color: var(--ok-border-color, var(--ion-border-color, #d7dbe0));
31
+ --color: var(--ok-text-color, var(--ion-text-color, #1f2933));
32
+ --label-color: var(--ok-color-medium, var(--ion-color-medium, #92949c));
33
+ --radius: 8px;
34
+ --inline-radius: 4px;
35
+ --mono: var(
36
+ --ok-font-mono,
37
+ ui-monospace,
38
+ 'SF Mono',
39
+ 'SFMono-Regular',
40
+ 'Menlo',
41
+ 'Consolas',
42
+ monospace
43
+ );
44
+ }
45
+
46
+ /* La variante inline no debe ocupar todo el ancho. */
47
+ :host([inline]) {
48
+ display: inline;
49
+ width: auto;
50
+ }
51
+
52
+ .wrap {
53
+ position: relative;
54
+ width: 100%;
55
+ box-sizing: border-box;
56
+ }
57
+
58
+ /* Bloque de código: superficie bordeada con scroll horizontal. */
59
+ .block {
60
+ display: block;
61
+ margin: 0;
62
+ background: var(--bg);
63
+ border: 1px solid var(--border-color);
64
+ border-radius: var(--radius);
65
+ padding: 14px 16px;
66
+ font-family: var(--mono);
67
+ font-size: 12.5px;
68
+ line-height: 1.6;
69
+ color: var(--color);
70
+ overflow-x: auto;
71
+ white-space: pre;
72
+ tab-size: 2;
73
+ }
74
+
75
+ /* Hueco a la derecha cuando hay botón de copiar, para no solapar. */
76
+ .block.has-copy {
77
+ padding-right: 44px;
78
+ }
79
+
80
+ /* Cabecera opcional con la etiqueta de lenguaje. */
81
+ .head {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 0.5rem;
85
+ padding: 0 0 6px;
86
+ }
87
+
88
+ .lang {
89
+ font-family: var(--mono);
90
+ font-size: 11px;
91
+ font-weight: 600;
92
+ letter-spacing: 0.04em;
93
+ text-transform: uppercase;
94
+ color: var(--label-color);
95
+ }
96
+
97
+ /* Botón de copiar flotante (esquina superior derecha del bloque). */
98
+ .copy {
99
+ position: absolute;
100
+ top: 6px;
101
+ right: 6px;
102
+ --padding-start: 6px;
103
+ --padding-end: 6px;
104
+ --padding-top: 4px;
105
+ --padding-bottom: 4px;
106
+ margin: 0;
107
+ height: 28px;
108
+ font-size: 11px;
109
+ }
110
+
111
+ /* Variante inline: pill sobre bg-3, sin borde. */
112
+ .inline {
113
+ display: inline;
114
+ padding: 1px 6px;
115
+ font-family: var(--mono);
116
+ font-size: 12px;
117
+ line-height: inherit;
118
+ color: var(--color);
119
+ background: var(--inline-bg);
120
+ border-radius: var(--inline-radius);
121
+ white-space: pre-wrap;
122
+ word-break: break-word;
123
+ }
124
+ `;
125
+ }
126
+ async handleCopy() {
127
+ try {
128
+ await navigator.clipboard.writeText(this.code);
129
+ this.copied = true;
130
+ window.setTimeout(() => {
131
+ this.copied = false;
132
+ }, 1600);
133
+ this.dispatchEvent(
134
+ new CustomEvent("ok-copy", {
135
+ detail: { code: this.code, ok: true },
136
+ bubbles: true,
137
+ composed: true
138
+ })
139
+ );
140
+ } catch {
141
+ this.dispatchEvent(
142
+ new CustomEvent("ok-copy", {
143
+ detail: { code: this.code, ok: false },
144
+ bubbles: true,
145
+ composed: true
146
+ })
147
+ );
148
+ }
149
+ }
150
+ render() {
151
+ if (this.inline) {
152
+ return html`<code class="inline">${this.code}</code>`;
153
+ }
154
+ const showCopy = this.copy;
155
+ return html`
156
+ <div class="wrap">
157
+ ${this.language ? html`<div class="head"><span class="lang">${this.language}</span></div>` : null}
158
+ <pre class="block ${showCopy ? "has-copy" : ""}"><code>${this.code}</code></pre>
159
+ ${showCopy ? html`<ion-button
160
+ class="copy"
161
+ size="small"
162
+ fill="solid"
163
+ color="medium"
164
+ aria-label="Copiar código"
165
+ @click=${this.handleCopy}
166
+ >${this.copied ? "Copiado" : "Copiar"}</ion-button
167
+ >` : null}
168
+ </div>
169
+ `;
170
+ }
171
+ }
172
+ __decorateClass([
173
+ property()
174
+ ], OkCode.prototype, "code");
175
+ __decorateClass([
176
+ property({ type: Boolean, reflect: true })
177
+ ], OkCode.prototype, "inline");
178
+ __decorateClass([
179
+ property()
180
+ ], OkCode.prototype, "language");
181
+ __decorateClass([
182
+ property({ type: Boolean })
183
+ ], OkCode.prototype, "copy");
184
+ __decorateClass([
185
+ state()
186
+ ], OkCode.prototype, "copied");
187
+ define("ok-code", OkCode);
188
+ export {
189
+ OkCode
190
+ };