@prolibu-suite/cobalt-core 0.9.3 → 0.9.4

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 (244) hide show
  1. package/custom-elements.json +375 -25
  2. package/dist/cjs/co-audio-player.cjs.entry.js +107 -18
  3. package/dist/cjs/co-avatar_2.cjs.entry.js +7 -31
  4. package/dist/cjs/co-button.cjs.entry.js +6 -4
  5. package/dist/cjs/co-calendar-month_2.cjs.entry.js +59 -2
  6. package/dist/cjs/co-checkbox.cjs.entry.js +10 -4
  7. package/dist/cjs/co-chip_2.cjs.entry.js +388 -18
  8. package/dist/cjs/co-color-picker.cjs.entry.js +36 -3
  9. package/dist/cjs/co-combobox.cjs.entry.js +44 -4
  10. package/dist/cjs/co-event-popover.cjs.entry.js +5 -2
  11. package/dist/cjs/co-file-field.cjs.entry.js +11 -5
  12. package/dist/cjs/co-icon-button.cjs.entry.js +5 -2
  13. package/dist/cjs/co-icon.cjs.entry.js +17 -1
  14. package/dist/cjs/co-input.cjs.entry.js +21 -1
  15. package/dist/cjs/co-loader.cjs.entry.js +1 -1
  16. package/dist/cjs/co-number-field.cjs.entry.js +22 -2
  17. package/dist/cjs/co-ref-field.cjs.entry.js +87 -13
  18. package/dist/cjs/co-select.cjs.entry.js +273 -8
  19. package/dist/cjs/co-tags-field.cjs.entry.js +1 -1
  20. package/dist/cjs/cobalt.cjs.js +1 -1
  21. package/dist/cjs/color-YhUkm10t.js +32 -0
  22. package/dist/cjs/fieldChip-BLzo9MEV.js +7 -0
  23. package/dist/cjs/index-DK-2Lzmf.js +4 -4
  24. package/dist/cjs/loader.cjs.js +1 -1
  25. package/dist/cobalt/cobalt.esm.js +1 -1
  26. package/dist/cobalt/p-0c463d9a.entry.js +1 -0
  27. package/dist/cobalt/p-0caa059c.entry.js +1 -0
  28. package/dist/cobalt/{p-3ced4269.entry.js → p-2828ecf6.entry.js} +1 -1
  29. package/dist/cobalt/p-2f88d151.entry.js +1 -0
  30. package/dist/cobalt/p-409f28bb.entry.js +1 -0
  31. package/dist/cobalt/{p-18af9e44.entry.js → p-568cfe8d.entry.js} +1 -1
  32. package/dist/cobalt/p-6bf814f9.entry.js +1 -0
  33. package/dist/cobalt/p-7ea87b75.entry.js +1 -0
  34. package/dist/cobalt/p-8b4b3589.entry.js +1 -0
  35. package/dist/cobalt/p-93c68029.entry.js +1 -0
  36. package/dist/cobalt/p-BG4WRR22.js +1 -0
  37. package/dist/cobalt/p-a2d31248.entry.js +1 -0
  38. package/dist/cobalt/p-bcedbd73.entry.js +1 -0
  39. package/dist/cobalt/p-dfd25f1d.entry.js +1 -0
  40. package/dist/cobalt/p-e364b8a5.entry.js +1 -0
  41. package/dist/cobalt/p-f00be11f.entry.js +1 -0
  42. package/dist/cobalt/p-f25967fb.entry.js +1 -0
  43. package/dist/cobalt/p-f5f1b463.entry.js +1 -0
  44. package/dist/cobalt/p-fef5db54.entry.js +1 -0
  45. package/dist/cobalt/p-mAGnZXRn.js +1 -0
  46. package/dist/collection/components/co-audio-player/co-audio-player.js +81 -17
  47. package/dist/collection/components/co-audio-player/co-audio-player.utils.js +27 -2
  48. package/dist/collection/components/co-autocomplete/co-autocomplete.js +1 -1
  49. package/dist/collection/components/co-avatar/co-avatar.js +5 -1
  50. package/dist/collection/components/co-avatar-group/co-avatar-group.js +1 -1
  51. package/dist/collection/components/co-button/co-button.css +7 -0
  52. package/dist/collection/components/co-button/co-button.js +7 -5
  53. package/dist/collection/components/co-calendar-month/co-calendar-month.js +59 -2
  54. package/dist/collection/components/co-checkbox/co-checkbox.css +22 -0
  55. package/dist/collection/components/co-checkbox/co-checkbox.js +9 -3
  56. package/dist/collection/components/co-checkbox-group/co-checkbox-group.js +1 -1
  57. package/dist/collection/components/co-color-picker/co-color-picker.css +7 -0
  58. package/dist/collection/components/co-color-picker/co-color-picker.js +35 -2
  59. package/dist/collection/components/co-combobox/co-combobox.css +7 -0
  60. package/dist/collection/components/co-combobox/co-combobox.js +44 -4
  61. package/dist/collection/components/co-command-search/co-command-search.js +1 -1
  62. package/dist/collection/components/co-data-table/co-data-table.js +1 -1
  63. package/dist/collection/components/co-date-range-picker/co-date-range-picker.js +2 -2
  64. package/dist/collection/components/co-event-popover/co-event-popover.js +5 -2
  65. package/dist/collection/components/co-file-field/co-file-field.js +8 -5
  66. package/dist/collection/components/co-icon/iconLoader.js +17 -1
  67. package/dist/collection/components/co-icon-button/co-icon-button.css +7 -0
  68. package/dist/collection/components/co-icon-button/co-icon-button.js +6 -3
  69. package/dist/collection/components/co-input/co-input.css +13 -0
  70. package/dist/collection/components/co-input/co-input.js +21 -0
  71. package/dist/collection/components/co-loader/co-loader.css +5 -3
  72. package/dist/collection/components/co-number-field/co-number-field.js +23 -2
  73. package/dist/collection/components/co-option-list/co-option-list.css +45 -2
  74. package/dist/collection/components/co-option-list/co-option-list.js +468 -18
  75. package/dist/collection/components/co-option-list/optionListLogic.js +25 -0
  76. package/dist/collection/components/co-popover/co-popover.js +1 -1
  77. package/dist/collection/components/co-radio-group/co-radio-group.js +1 -1
  78. package/dist/collection/components/co-ref-field/co-ref-field.css +49 -0
  79. package/dist/collection/components/co-ref-field/co-ref-field.js +240 -12
  80. package/dist/collection/components/co-segmented-bar/co-segmented-bar.js +1 -1
  81. package/dist/collection/components/co-select/co-select.css +19 -0
  82. package/dist/collection/components/co-select/co-select.js +275 -9
  83. package/dist/collection/components/co-sidebar-nav/co-sidebar-nav.js +1 -1
  84. package/dist/collection/components/co-toast/co-toast.js +2 -2
  85. package/dist/collection/components/co-transfer-list/co-transfer-list.js +1 -1
  86. package/dist/collection/utils/fieldChip.js +1 -1
  87. package/dist/collection/utils/fileI18n.js +3 -0
  88. package/dist/components/co-accordion.js +1 -1
  89. package/dist/components/co-action-bar.js +1 -1
  90. package/dist/components/co-audio-player.js +1 -1
  91. package/dist/components/co-autocomplete.js +1 -1
  92. package/dist/components/co-avatar-group.js +1 -1
  93. package/dist/components/co-avatar.js +1 -1
  94. package/dist/components/co-breadcrumb-item.js +1 -1
  95. package/dist/components/co-breadcrumb.js +1 -1
  96. package/dist/components/co-button.js +1 -1
  97. package/dist/components/co-calendar-month.js +1 -1
  98. package/dist/components/co-calendar-timegrid.js +1 -1
  99. package/dist/components/co-calendar.js +1 -1
  100. package/dist/components/co-carousel.js +1 -1
  101. package/dist/components/co-checkbox-group.js +1 -1
  102. package/dist/components/co-checkbox.js +1 -1
  103. package/dist/components/co-chip.js +1 -1
  104. package/dist/components/co-color-picker.js +1 -1
  105. package/dist/components/co-column-picker.js +1 -1
  106. package/dist/components/co-combobox.js +1 -1
  107. package/dist/components/co-command-search.js +1 -1
  108. package/dist/components/co-data-table-pagination.js +1 -1
  109. package/dist/components/co-data-table.js +1 -1
  110. package/dist/components/co-date-field.js +1 -1
  111. package/dist/components/co-date-picker.js +1 -1
  112. package/dist/components/co-date-range-picker.js +1 -1
  113. package/dist/components/co-date-range.js +1 -1
  114. package/dist/components/co-date-time-picker.js +1 -1
  115. package/dist/components/co-drawer.js +1 -1
  116. package/dist/components/co-drive-card.js +1 -1
  117. package/dist/components/co-drop-zone.js +1 -1
  118. package/dist/components/co-emoji-picker.js +1 -1
  119. package/dist/components/co-empty-state.js +1 -1
  120. package/dist/components/co-event-calendar.js +1 -1
  121. package/dist/components/co-event-popover.js +1 -1
  122. package/dist/components/co-fab.js +1 -1
  123. package/dist/components/co-file-field.js +1 -1
  124. package/dist/components/co-file-type-tile.js +1 -1
  125. package/dist/components/co-helper-text.js +1 -1
  126. package/dist/components/co-icon-button.js +1 -1
  127. package/dist/components/co-icon.js +1 -1
  128. package/dist/components/co-input.js +1 -1
  129. package/dist/components/co-json-field.js +1 -1
  130. package/dist/components/co-lightbox.js +1 -1
  131. package/dist/components/co-list-footer.js +1 -1
  132. package/dist/components/co-list-item.js +1 -1
  133. package/dist/components/co-loader.js +1 -1
  134. package/dist/components/co-menu-item.js +1 -1
  135. package/dist/components/co-modal.js +1 -1
  136. package/dist/components/co-number-field.js +1 -1
  137. package/dist/components/co-option-list.js +1 -1
  138. package/dist/components/co-page-header.js +1 -1
  139. package/dist/components/co-phone-field.js +1 -1
  140. package/dist/components/co-radio-group.js +1 -1
  141. package/dist/components/co-rating.js +1 -1
  142. package/dist/components/co-ref-field.js +1 -1
  143. package/dist/components/co-rich-text-editor.js +1 -1
  144. package/dist/components/co-rich-text-toolbar.js +1 -1
  145. package/dist/components/co-select.js +1 -1
  146. package/dist/components/co-sidebar-nav.js +1 -1
  147. package/dist/components/co-slider.js +1 -1
  148. package/dist/components/co-switch.js +1 -1
  149. package/dist/components/co-tab.js +1 -1
  150. package/dist/components/co-tags-field.js +1 -1
  151. package/dist/components/co-textarea.js +1 -1
  152. package/dist/components/co-toast.js +1 -1
  153. package/dist/components/co-toggle-button.js +1 -1
  154. package/dist/components/co-transfer-list.js +1 -1
  155. package/dist/components/co-video.js +1 -1
  156. package/dist/components/{p-CklsvHeL.js → p-B0Dc-I_C.js} +1 -1
  157. package/dist/components/{p-DzoNGEDU.js → p-BH184F0X.js} +1 -1
  158. package/dist/components/{p-BGD3oQ73.js → p-BHYtxTzM.js} +1 -1
  159. package/dist/components/{p-mom5kiBa.js → p-BTQEg3UO.js} +1 -1
  160. package/dist/components/p-BZIB4JDs.js +1 -0
  161. package/dist/components/p-Bbny3WUc.js +1 -0
  162. package/dist/components/{p-D5QJpmqf.js → p-Bxk96Zgc.js} +1 -1
  163. package/dist/components/p-C1Kv3zYZ.js +1 -0
  164. package/dist/components/p-C4U2tbRp.js +1 -0
  165. package/dist/components/{p-CyMaOb1j.js → p-CA_JAK9s.js} +1 -1
  166. package/dist/components/{p-AvSaw4Ua.js → p-CR0izsJC.js} +1 -1
  167. package/dist/components/p-CuZXTvDM.js +1 -0
  168. package/dist/components/{p-_131DL6m.js → p-D4qzGifz.js} +1 -1
  169. package/dist/components/{p-BsQQ7Mki.js → p-D5EdMieb.js} +1 -1
  170. package/dist/components/p-Dabi98BI.js +1 -0
  171. package/dist/components/p-DbipMAyT.js +1 -0
  172. package/dist/components/p-DgUBcJIS.js +1 -0
  173. package/dist/components/p-Dj1EZ5qD.js +1 -0
  174. package/dist/components/{p-DkmKOF60.js → p-P8wvwhM2.js} +1 -1
  175. package/dist/components/{p-nwy6X1QT.js → p-hfGFsvaD.js} +1 -1
  176. package/dist/esm/co-audio-player.entry.js +107 -18
  177. package/dist/esm/co-avatar_2.entry.js +6 -30
  178. package/dist/esm/co-button.entry.js +6 -4
  179. package/dist/esm/co-calendar-month_2.entry.js +59 -2
  180. package/dist/esm/co-checkbox.entry.js +10 -4
  181. package/dist/esm/co-chip_2.entry.js +388 -18
  182. package/dist/esm/co-color-picker.entry.js +36 -3
  183. package/dist/esm/co-combobox.entry.js +44 -4
  184. package/dist/esm/co-event-popover.entry.js +5 -2
  185. package/dist/esm/co-file-field.entry.js +11 -5
  186. package/dist/esm/co-icon-button.entry.js +5 -2
  187. package/dist/esm/co-icon.entry.js +17 -1
  188. package/dist/esm/co-input.entry.js +21 -1
  189. package/dist/esm/co-loader.entry.js +1 -1
  190. package/dist/esm/co-number-field.entry.js +22 -2
  191. package/dist/esm/co-ref-field.entry.js +87 -13
  192. package/dist/esm/co-select.entry.js +273 -8
  193. package/dist/esm/co-tags-field.entry.js +1 -1
  194. package/dist/esm/cobalt.js +1 -1
  195. package/dist/esm/color-BG4WRR22.js +30 -0
  196. package/dist/esm/fieldChip-DsAfq1e0.js +5 -0
  197. package/dist/esm/index-CrtiywTB.js +4 -4
  198. package/dist/esm/loader.js +1 -1
  199. package/dist/types/components/co-audio-player/co-audio-player.d.ts +13 -0
  200. package/dist/types/components/co-audio-player/co-audio-player.utils.d.ts +18 -1
  201. package/dist/types/components/co-button/co-button.d.ts +3 -1
  202. package/dist/types/components/co-calendar-month/co-calendar-month.d.ts +7 -0
  203. package/dist/types/components/co-checkbox/co-checkbox.d.ts +1 -0
  204. package/dist/types/components/co-color-picker/co-color-picker.d.ts +15 -0
  205. package/dist/types/components/co-combobox/co-combobox.d.ts +17 -0
  206. package/dist/types/components/co-icon-button/co-icon-button.d.ts +3 -0
  207. package/dist/types/components/co-input/co-input.d.ts +9 -0
  208. package/dist/types/components/co-number-field/co-number-field.d.ts +9 -0
  209. package/dist/types/components/co-option-list/co-option-list.d.ts +79 -0
  210. package/dist/types/components/co-option-list/optionListLogic.d.ts +23 -1
  211. package/dist/types/components/co-ref-field/co-ref-field.d.ts +46 -0
  212. package/dist/types/components/co-select/co-select.d.ts +67 -0
  213. package/dist/types/components.d.ts +110 -4
  214. package/dist/types/utils/fieldChip.d.ts +5 -0
  215. package/dist/types/utils/fileI18n.d.ts +1 -0
  216. package/package.json +3 -3
  217. package/dist/cjs/fieldChip-xNJPEFcS.js +0 -7
  218. package/dist/cobalt/p-02ac492f.entry.js +0 -1
  219. package/dist/cobalt/p-0ccb672b.entry.js +0 -1
  220. package/dist/cobalt/p-2d81b292.entry.js +0 -1
  221. package/dist/cobalt/p-4a503187.entry.js +0 -1
  222. package/dist/cobalt/p-55566b37.entry.js +0 -1
  223. package/dist/cobalt/p-680d3db8.entry.js +0 -1
  224. package/dist/cobalt/p-7457aa89.entry.js +0 -1
  225. package/dist/cobalt/p-77719335.entry.js +0 -1
  226. package/dist/cobalt/p-8f600512.entry.js +0 -1
  227. package/dist/cobalt/p-CQAYgqA6.js +0 -1
  228. package/dist/cobalt/p-a02db338.entry.js +0 -1
  229. package/dist/cobalt/p-a51d8775.entry.js +0 -1
  230. package/dist/cobalt/p-aafb9d11.entry.js +0 -1
  231. package/dist/cobalt/p-b28dc0fe.entry.js +0 -1
  232. package/dist/cobalt/p-c6b9b9d6.entry.js +0 -1
  233. package/dist/cobalt/p-d4debd6f.entry.js +0 -1
  234. package/dist/cobalt/p-d629fb4e.entry.js +0 -1
  235. package/dist/components/p-B3TkpLNh.js +0 -1
  236. package/dist/components/p-B7hvddnu.js +0 -1
  237. package/dist/components/p-BT5tSgZa.js +0 -1
  238. package/dist/components/p-CXLX8DpD.js +0 -1
  239. package/dist/components/p-DTvb412h.js +0 -1
  240. package/dist/components/p-DvhR3H61.js +0 -1
  241. package/dist/components/p-KWTokD0C.js +0 -1
  242. package/dist/components/p-Q6caHt79.js +0 -1
  243. package/dist/components/p-WWlZiln1.js +0 -1
  244. package/dist/esm/fieldChip-BZDUy9Z-.js +0 -5
@@ -1,5 +1,5 @@
1
1
  import { h } from "@stencil/core";
2
- import { formatTime, resamplePeaks, fallbackPeaks, scrubFraction } from "./co-audio-player.utils";
2
+ import { formatTime, resamplePeaks, fallbackPeaks, scrubFraction, resolveDuration } from "./co-audio-player.utils";
3
3
  export class CoAudioPlayer {
4
4
  constructor() {
5
5
  /** Compact variant: narrower, hides the download button. */
@@ -21,7 +21,10 @@ export class CoAudioPlayer {
21
21
  this.isLoading = false;
22
22
  this.hasError = false;
23
23
  this.currentTime = 0;
24
- this.duration = 0;
24
+ /** Playback length in seconds, or `NaN` while it is not known — which is the
25
+ * normal state for a `MediaRecorder` blob until the decode lands. Everything
26
+ * that does arithmetic with it goes through `knownDuration`. */
27
+ this.duration = NaN;
25
28
  this.peaks = [];
26
29
  /** Measured wave width (px); 0 until the ResizeObserver reports it. */
27
30
  this.containerWidth = 0;
@@ -29,7 +32,13 @@ export class CoAudioPlayer {
29
32
  this.playbackRate = 1;
30
33
  // Decoded mono PCM, kept so bars can be re-resampled on resize without re-fetching.
31
34
  this.rawChannelData = null;
32
- this.onMeta = () => { var _a; this.duration = ((_a = this.audio) === null || _a === void 0 ? void 0 : _a.duration) || 0; };
35
+ // Length reported by the WebAudio decode; NaN when nothing has decoded (yet).
36
+ // It is the only trustworthy duration for a container without a duration header.
37
+ this.decodedDuration = NaN;
38
+ // Bumped by every init(); an in-flight extractPeaks whose token no longer matches
39
+ // belongs to a superseded `src` and must not write peaks/duration.
40
+ this.loadToken = 0;
41
+ this.onMeta = () => { this.syncDuration(); };
33
42
  this.onCanPlay = () => { this.isLoading = false; };
34
43
  this.onWaiting = () => { this.isLoading = true; };
35
44
  this.onTime = () => { var _a; this.currentTime = ((_a = this.audio) === null || _a === void 0 ? void 0 : _a.currentTime) || 0; };
@@ -56,11 +65,15 @@ export class CoAudioPlayer {
56
65
  if (this.audio)
57
66
  this.audio.playbackRate = this.playbackRate;
58
67
  };
68
+ // Scrubbing needs a real total to map the click fraction onto: with an unknown
69
+ // duration `frac * duration` is Infinity (or NaN at frac 0), and assigning that
70
+ // to `currentTime` throws. No total → no scrub.
59
71
  this.seekAt = (clientX) => {
60
- if (!this.audio || !this.waveEl || !this.duration)
72
+ const total = this.knownDuration;
73
+ if (!this.audio || !this.waveEl || !total)
61
74
  return;
62
75
  const frac = scrubFraction(clientX, this.waveEl.getBoundingClientRect());
63
- this.audio.currentTime = frac * this.duration;
76
+ this.audio.currentTime = frac * total;
64
77
  this.currentTime = this.audio.currentTime;
65
78
  };
66
79
  this.onWaveKey = (e) => {
@@ -69,17 +82,29 @@ export class CoAudioPlayer {
69
82
  this.toggle();
70
83
  return;
71
84
  }
72
- if (!this.audio || !this.duration)
85
+ if (!this.audio)
73
86
  return;
87
+ // Relative nudges stay available even with an unknown duration (the media
88
+ // element clamps a forward seek to its own seekable end); only the upper
89
+ // clamp needs a total, and both operands stay finite either way.
90
+ const total = this.knownDuration;
91
+ const at = this.audio.currentTime;
74
92
  if (e.key === 'ArrowRight')
75
- this.audio.currentTime = Math.min(this.duration, this.audio.currentTime + 5);
93
+ this.audio.currentTime = total ? Math.min(total, at + 5) : at + 5;
76
94
  else if (e.key === 'ArrowLeft')
77
- this.audio.currentTime = Math.max(0, this.audio.currentTime - 5);
95
+ this.audio.currentTime = Math.max(0, at - 5);
78
96
  else
79
97
  return;
80
98
  this.currentTime = this.audio.currentTime;
81
99
  };
82
100
  }
101
+ /** Duration usable for arithmetic (seek target, progress %, ARIA): `0` when it
102
+ * is not known, so every caller can bail on a plain falsy check instead of
103
+ * letting `Infinity`/`NaN` reach `audio.currentTime` — that setter takes a
104
+ * restricted `double` and THROWS a TypeError on a non-finite value. */
105
+ get knownDuration() {
106
+ return Number.isFinite(this.duration) && this.duration > 0 ? this.duration : 0;
107
+ }
83
108
  /** Bar count: derived from the measured width (V1-style density) once known,
84
109
  * else the `bars` fallback. Floored at 24 so a narrow player still reads as a wave. */
85
110
  get effectiveBars() {
@@ -117,6 +142,7 @@ export class CoAudioPlayer {
117
142
  }
118
143
  disconnectedCallback() {
119
144
  var _a;
145
+ this.loadToken++; // orphan any in-flight decode so it can't write to a dead instance
120
146
  this.teardown();
121
147
  (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
122
148
  this.resizeObserver = undefined;
@@ -131,16 +157,18 @@ export class CoAudioPlayer {
131
157
  }
132
158
  init() {
133
159
  this.teardown();
160
+ const token = ++this.loadToken;
134
161
  this.rawChannelData = null;
162
+ this.decodedDuration = NaN;
135
163
  this.peaks = fallbackPeaks(this.effectiveBars); // draw immediately; refine with real peaks
136
164
  this.currentTime = 0;
137
- this.duration = 0;
165
+ this.duration = NaN; // unknown until `loadedmetadata` or the decode says otherwise
138
166
  this.playbackRate = 1; // a fresh source starts at 1x (new <audio> defaults to 1)
139
167
  this.hasError = false;
140
168
  if (!this.src)
141
169
  return;
142
170
  this.isLoading = true;
143
- this.extractPeaks(this.src);
171
+ this.extractPeaks(this.src, token);
144
172
  if (typeof Audio === 'undefined')
145
173
  return; // test/SSR guard
146
174
  const el = new Audio();
@@ -169,22 +197,52 @@ export class CoAudioPlayer {
169
197
  }
170
198
  this.isPlaying = false;
171
199
  }
172
- async extractPeaks(url) {
200
+ async extractPeaks(url, token) {
201
+ let ctx;
173
202
  try {
174
203
  const Ctx = window.AudioContext || window.webkitAudioContext;
175
204
  if (!Ctx || typeof fetch === 'undefined')
176
205
  return; // keep fallback
177
- const ctx = new Ctx();
206
+ ctx = new Ctx();
178
207
  const buf = await (await fetch(url)).arrayBuffer();
179
208
  const audioBuf = await ctx.decodeAudioData(buf);
209
+ if (token !== this.loadToken)
210
+ return; // a newer src superseded this decode
180
211
  this.rawChannelData = audioBuf.getChannelData(0);
212
+ this.decodedDuration = audioBuf.duration;
181
213
  this.peaks = resamplePeaks(this.rawChannelData, this.effectiveBars);
182
- ctx.close();
183
214
  }
184
215
  catch (_a) {
216
+ if (token !== this.loadToken)
217
+ return;
218
+ // Undecodable (codec WebAudio can't handle, CORS, network): keep the
219
+ // deterministic fallback wave and let <audio> own the duration — playback,
220
+ // seek and the time readout all still work, or degrade to `--:--`.
185
221
  this.rawChannelData = null;
222
+ this.decodedDuration = NaN;
186
223
  this.peaks = fallbackPeaks(this.effectiveBars); // stays functional
187
224
  }
225
+ finally {
226
+ // Always release the context. Chrome caps concurrent AudioContexts per
227
+ // document, so leaking one per failed decode would eventually stop every
228
+ // later player on the page from decoding at all.
229
+ try {
230
+ await (ctx === null || ctx === void 0 ? void 0 : ctx.close());
231
+ }
232
+ catch ( /* already closed */_b) { /* already closed */ }
233
+ }
234
+ if (token === this.loadToken)
235
+ this.syncDuration();
236
+ }
237
+ /** Re-derive the shown duration from whichever source knows it. Called from both
238
+ * `loadedmetadata` and the end of the decode, because either can land first. */
239
+ syncDuration() {
240
+ var _a, _b;
241
+ const next = resolveDuration((_b = (_a = this.audio) === null || _a === void 0 ? void 0 : _a.duration) !== null && _b !== void 0 ? _b : NaN, this.decodedDuration);
242
+ // Object.is, not !==: Stencil's @State setter re-renders on any `oldVal !== newVal`,
243
+ // and NaN !== NaN — so a plain assignment would re-render on every no-op sync.
244
+ if (!Object.is(next, this.duration))
245
+ this.duration = next;
188
246
  }
189
247
  async download() {
190
248
  if (!this.src || typeof fetch === 'undefined')
@@ -205,14 +263,20 @@ export class CoAudioPlayer {
205
263
  render() {
206
264
  const H = 40, UNIT = 3, W = 1.8, R = 0.6;
207
265
  const vbW = Math.max(1, this.peaks.length * UNIT);
208
- const pct = this.duration ? (this.currentTime / this.duration) * 100 : 0;
209
- const bar = (hFrac, i, played = false) => (h("rect", { key: 'ffe5ce97626e52c12e60b953b62d237f14acb582', x: i * UNIT, y: H / 2 - (hFrac * H) / 2, width: W, height: Math.max(hFrac * H, 2), rx: R, class: { 'co-audio-player__bar': true, 'co-audio-player__bar--played': played } }));
210
- return (h("div", { key: '012710aded6ca183e80d7fde087cb0fdf413249b', class: {
266
+ const total = this.knownDuration; // 0 while unknown → no progress fill, no ARIA max
267
+ const pct = total ? (this.currentTime / total) * 100 : 0;
268
+ // Undefined drops the attribute (Stencil removes null/undefined attrs). An
269
+ // `aria-valuemax="Infinity"`/`"NaN"` is invalid, and a stand-in `"0"` would sit
270
+ // below `aria-valuenow` the moment playback advances. ARIA 1.2 makes valuemax
271
+ // optional for `slider`, so leaving it off while unknown is well-formed.
272
+ const ariaMax = total ? Math.floor(total) : undefined;
273
+ const bar = (hFrac, i, played = false) => (h("rect", { key: '997dcefc32f5188c41afa0a7b5135c70cceeed1d', x: i * UNIT, y: H / 2 - (hFrac * H) / 2, width: W, height: Math.max(hFrac * H, 2), rx: R, class: { 'co-audio-player__bar': true, 'co-audio-player__bar--played': played } }));
274
+ return (h("div", { key: '8ac91ab3f8eb417df5e7cf4d1e016d6ca7eb45a3', class: {
211
275
  'co-audio-player': true,
212
276
  'co-audio-player--compact': this.compact,
213
277
  'co-audio-player--error': this.hasError,
214
278
  'co-audio-player--playing': this.isPlaying,
215
- }, part: "player" }, h("button", { key: '2505b88e97e73fd39aa31e3dad5e7fe0c1b94c80', type: "button", class: "co-audio-player__play", part: "play", "aria-label": this.hasError ? 'Audio no disponible' : this.isPlaying ? 'Pausar' : 'Reproducir', "aria-busy": this.isLoading ? 'true' : 'false', disabled: this.hasError || !this.src, onClick: this.toggle }, h("co-icon", { key: 'e0e0d2dd5f6e0578ba80a3c235e5c221a6e8fe21', name: this.hasError ? 'warning-circle' : this.isPlaying ? 'pause' : 'play', weight: "fill", size: this.compact ? 12 : 14 })), h("div", { key: 'd2b32119c69c64d2f68d469fe4ae056780446322', class: "co-audio-player__wave", part: "wave", role: "slider", tabindex: "0", "aria-label": "Progreso de reproducci\u00F3n", "aria-valuemin": 0, "aria-valuemax": Math.floor(this.duration), "aria-valuenow": Math.floor(this.currentTime), ref: (el) => (this.waveEl = el), onClick: (e) => this.seekAt(e.clientX), onKeyDown: this.onWaveKey }, h("svg", { key: 'fb9643df152d2d0b119580954f639cb3a9c2cd66', class: "co-audio-player__svg", viewBox: `0 0 ${vbW} ${H}`, preserveAspectRatio: "none", "aria-hidden": "true" }, this.peaks.map((hFrac, i) => bar(hFrac, i)), h("clipPath", { key: 'c2901cfe7c7e910bd53c8c92894b2d2395cde270', id: "co-ap-clip" }, h("rect", { key: '8129a85201e09ba6646b08ed0075798b0a0b0dfc', x: "0", y: "0", width: (pct / 100) * vbW, height: H })), h("g", { key: '69bc1780122d48f1a545d1c59bf7ed6536285ed2', "clip-path": "url(#co-ap-clip)" }, this.peaks.map((hFrac, i) => bar(hFrac, i, true))))), h("span", { key: '3747622619bd81545f85e7eb2f9c19b1aca6e7c2', class: "co-audio-player__time", part: "time" }, formatTime(this.currentTime), " / ", formatTime(this.duration)), !this.compact && (h("button", { key: '84341856fb2e0b6a094c673a727370b148190e30', type: "button", class: "co-audio-player__speed", part: "speed", "aria-label": `Velocidad de reproducción ${this.playbackRate}x`, onClick: this.cycleSpeed }, this.playbackRate, "x")), !this.compact && !this.noDownload && (h("button", { key: 'd6767d185317f20299b40c4050a806d88165682f', type: "button", class: "co-audio-player__dl", part: "download", "aria-label": "Descargar", onClick: () => this.download() }, h("co-icon", { key: 'a0f0a09cdf705ecf15e0156cf93011f89f41ffa5', name: "download-simple", size: 14 })))));
279
+ }, part: "player" }, h("button", { key: '0e5897dc64c91ee67432f17d7b0d57a27f44cd0e', type: "button", class: "co-audio-player__play", part: "play", "aria-label": this.hasError ? 'Audio no disponible' : this.isPlaying ? 'Pausar' : 'Reproducir', "aria-busy": this.isLoading ? 'true' : 'false', disabled: this.hasError || !this.src, onClick: this.toggle }, h("co-icon", { key: '4843751a2a3709942a49ae03ff9ba0eb08ab7d97', name: this.hasError ? 'warning-circle' : this.isPlaying ? 'pause' : 'play', weight: "fill", size: this.compact ? 12 : 14 })), h("div", { key: 'b7aebd0e0c5197b0dec6257bbec7c9dd8dd55d6f', class: "co-audio-player__wave", part: "wave", role: "slider", tabindex: "0", "aria-label": "Progreso de reproducci\u00F3n", "aria-valuemin": 0, "aria-valuemax": ariaMax, "aria-valuenow": Math.floor(this.currentTime), ref: (el) => (this.waveEl = el), onClick: (e) => this.seekAt(e.clientX), onKeyDown: this.onWaveKey }, h("svg", { key: 'a504d058a27bb33dd57733ec32db306eb0ed87e9', class: "co-audio-player__svg", viewBox: `0 0 ${vbW} ${H}`, preserveAspectRatio: "none", "aria-hidden": "true" }, this.peaks.map((hFrac, i) => bar(hFrac, i)), h("clipPath", { key: '9cfa69ba28801893baf62c8f66b18a31319fdebe', id: "co-ap-clip" }, h("rect", { key: '6e5cb34baeb2b7dddd6ef7e94f8585ef912e0315', x: "0", y: "0", width: (pct / 100) * vbW, height: H })), h("g", { key: '4b78a29dde762cabc85d7921aacbcd261d8fa6b5', "clip-path": "url(#co-ap-clip)" }, this.peaks.map((hFrac, i) => bar(hFrac, i, true))))), h("span", { key: '9067d22b0e2994e46ef49c60356292ec59f61a21', class: "co-audio-player__time", part: "time" }, formatTime(this.currentTime), " / ", formatTime(this.duration)), !this.compact && (h("button", { key: 'f6149a9996e6ce15230221c2f6fcec95fec5f485', type: "button", class: "co-audio-player__speed", part: "speed", "aria-label": `Velocidad de reproducción ${this.playbackRate}x`, onClick: this.cycleSpeed }, this.playbackRate, "x")), !this.compact && !this.noDownload && (h("button", { key: '73d291d31d92bcb0e18e230fe0ac35d3ab4077b2', type: "button", class: "co-audio-player__dl", part: "download", "aria-label": "Descargar", onClick: () => this.download() }, h("co-icon", { key: '9ab09f5d6b975f4f5544019f70bd2ca1ee521068', name: "download-simple", size: 14 })))));
216
280
  }
217
281
  static get is() { return "co-audio-player"; }
218
282
  static get encapsulation() { return "shadow"; }
@@ -1,10 +1,35 @@
1
- /** Seconds → "m:ss" (floors; 0 for NaN/negative). */
1
+ /** Rendered in place of `m:ss` while a time is not known.
2
+ *
3
+ * Not an error state: `<audio>.duration` is `Infinity` for any container whose
4
+ * header carries no duration — every blob `MediaRecorder` produces, i.e. every
5
+ * freshly recorded voice note — and Chrome keeps it that way until the clip has
6
+ * been played end to end. `0:00` there would read as a zero-length recording;
7
+ * `--:--` reads as "not known yet", which is the truth. */
8
+ export const UNKNOWN_TIME = '--:--';
9
+ /** Seconds → "m:ss" (floors). Anything that is not a finite, non-negative number
10
+ * (`Infinity`, `NaN`, negatives) → `UNKNOWN_TIME` — never `"Infinity:NaN"`. */
2
11
  export function formatTime(seconds) {
3
- const s = Math.max(0, Math.floor(seconds || 0));
12
+ if (!Number.isFinite(seconds) || seconds < 0)
13
+ return UNKNOWN_TIME;
14
+ const s = Math.floor(seconds);
4
15
  const m = Math.floor(s / 60);
5
16
  const sec = s % 60;
6
17
  return `${m}:${sec.toString().padStart(2, '0')}`;
7
18
  }
19
+ /** Picks the duration to trust (seconds), or `NaN` when neither source knows it.
20
+ *
21
+ * `<audio>.duration` is authoritative *when it is a real number*. It is not for
22
+ * a duration-less container (see `UNKNOWN_TIME`), where it reports `Infinity`.
23
+ * The WebAudio decode the player already runs to draw the waveform does know the
24
+ * real length (`AudioBuffer.duration`), so it is the fallback. `0` from either
25
+ * source means "empty/unknown" too, so it never wins. */
26
+ export function resolveDuration(fromAudio, fromDecode) {
27
+ if (Number.isFinite(fromAudio) && fromAudio > 0)
28
+ return fromAudio;
29
+ if (Number.isFinite(fromDecode) && fromDecode > 0)
30
+ return fromDecode;
31
+ return NaN;
32
+ }
8
33
  /** Downsample raw PCM to `bars` peak heights, each normalized to a 0..1 fraction. */
9
34
  export function resamplePeaks(channelData, bars) {
10
35
  const len = channelData.length;
@@ -418,7 +418,7 @@ export class CoAutocomplete {
418
418
  "references": {
419
419
  "AutocompleteOption": {
420
420
  "location": "local",
421
- "path": "/Users/agox/worktrees/cobalt-release-093/packages/core/src/components/co-autocomplete/co-autocomplete.tsx",
421
+ "path": "/Users/agox/Documents/Prolibu/prolibu-cobalt/packages/core/src/components/co-autocomplete/co-autocomplete.tsx",
422
422
  "id": "src/components/co-autocomplete/co-autocomplete.tsx::AutocompleteOption"
423
423
  }
424
424
  }
@@ -37,6 +37,10 @@ export class CoAvatar {
37
37
  const iconSize = (_b = ICON_SIZE_MAP[this.size]) !== null && _b !== void 0 ? _b : 24;
38
38
  const fontSize = (_c = FONT_SIZE_MAP[this.size]) !== null && _c !== void 0 ? _c : 'var(--co-font-size-18)';
39
39
  const showImage = this.type === 'image' && this.src && !this.imgFailed;
40
+ // Broken/missing image without initials falls back to the ICON (default
41
+ // "user") — never an empty disc. With `text` present, initials win (they
42
+ // identify the record better than a generic glyph).
43
+ const showIcon = this.type === 'icon' || (this.type === 'image' && !showImage && !this.text);
40
44
  const ariaLabel = this.type === 'text'
41
45
  ? this.text
42
46
  : this.type === 'image'
@@ -50,7 +54,7 @@ export class CoAvatar {
50
54
  'co-avatar--light': !!this.color && isLightColor(this.color),
51
55
  }, style: Object.assign(Object.assign({ width: `var(--co-avatar-size, ${dim}px)`, height: `var(--co-avatar-size, ${dim}px)`, fontSize }, (this.color ? { background: this.color } : {})), (this.ringColor
52
56
  ? { '--co-avatar-border-color': this.ringColor, '--co-avatar-border-width': '2px' }
53
- : {})), part: "avatar", role: "img", "aria-label": ariaLabel }, showImage ? (h("img", { class: "co-avatar__img", src: this.src, alt: (_g = (_f = this.alt) !== null && _f !== void 0 ? _f : this.text) !== null && _g !== void 0 ? _g : '', onError: this.handleImgError })) : this.type === 'icon' ? (h("co-icon", { name: this.icon, weight: this.iconWeight, size: iconSize })) : (h("span", { class: "co-avatar__text" }, this.text))));
57
+ : {})), part: "avatar", role: "img", "aria-label": ariaLabel }, showImage ? (h("img", { class: "co-avatar__img", src: this.src, alt: (_g = (_f = this.alt) !== null && _f !== void 0 ? _f : this.text) !== null && _g !== void 0 ? _g : '', onError: this.handleImgError })) : showIcon ? (h("co-icon", { name: this.icon, weight: this.iconWeight, size: iconSize })) : (h("span", { class: "co-avatar__text" }, this.text))));
54
58
  // No tooltip when `name` is unset/empty — renders exactly as before, no wrapper.
55
59
  if (!this.name)
56
60
  return disc;
@@ -87,7 +87,7 @@ export class CoAvatarGroup {
87
87
  "references": {
88
88
  "CoAvatarGroupItem": {
89
89
  "location": "local",
90
- "path": "/Users/agox/worktrees/cobalt-release-093/packages/core/src/components/co-avatar-group/co-avatar-group.tsx",
90
+ "path": "/Users/agox/Documents/Prolibu/prolibu-cobalt/packages/core/src/components/co-avatar-group/co-avatar-group.tsx",
91
91
  "id": "src/components/co-avatar-group/co-avatar-group.tsx::CoAvatarGroupItem"
92
92
  }
93
93
  }
@@ -2,6 +2,13 @@
2
2
  display: inline-flex;
3
3
  }
4
4
 
5
+ /* Disabled must block clicks AT THE HOST: the inner button's
6
+ pointer-events:none re-targets clicks to the host element, where consumer
7
+ listeners (Vue @click) live — without this, "disabled" buttons still fire. */
8
+ :host([disabled]) {
9
+ pointer-events: none;
10
+ }
11
+
5
12
  .co-button {
6
13
  display: inline-flex;
7
14
  align-items: center;
@@ -22,7 +22,9 @@ export class CoButton {
22
22
  * `variant="danger" surface="dark"`.
23
23
  */
24
24
  this.surface = 'light';
25
- /** Disabled state */
25
+ /** Disabled state. Reflected so :host([disabled]) can kill pointer-events —
26
+ * the inner button's own pointer-events:none re-targets clicks to the host,
27
+ * where consumers' click listeners live. */
26
28
  this.disabled = false;
27
29
  /** Icon weight applied to both iconLeft and iconRight */
28
30
  this.iconWeight = 'regular';
@@ -53,7 +55,7 @@ export class CoButton {
53
55
  customStyle.background = this.color;
54
56
  if (this.textColor)
55
57
  customStyle.color = this.textColor;
56
- return (h("button", { key: 'e765a1fcb3b4efcc7c43e155015b3c178a536d3c', class: {
58
+ return (h("button", { key: '651f8d079c09cf21d076aa20564a462a15ca8c4a', class: {
57
59
  'co-button': true,
58
60
  [`co-button--${this.variant}`]: true,
59
61
  [`co-button--${this.shape}`]: true,
@@ -63,7 +65,7 @@ export class CoButton {
63
65
  'co-button--disabled': this.disabled,
64
66
  }, style: Object.keys(customStyle).length ? customStyle : undefined, type: this.type, disabled: this.disabled, "aria-label": this.ariaLabelText || undefined, onClick: this.handleClick, part: "button" }, this.iconLeft
65
67
  ? h("co-icon", { name: this.iconLeft, weight: this.iconWeight, size: iconSize })
66
- : null, h("slot", { key: '8dd3e816276a67ba6072eeed1ad34a982b60389e' }, this.label), this.iconRight
68
+ : null, h("slot", { key: 'b8c5b230a473fe4a57aa087533186e410db64bed' }, this.label), this.iconRight
67
69
  ? h("co-icon", { name: this.iconRight, weight: this.iconWeight, size: iconSize })
68
70
  : null));
69
71
  }
@@ -193,11 +195,11 @@ export class CoButton {
193
195
  "optional": false,
194
196
  "docs": {
195
197
  "tags": [],
196
- "text": "Disabled state"
198
+ "text": "Disabled state. Reflected so :host([disabled]) can kill pointer-events \u2014\nthe inner button's own pointer-events:none re-targets clicks to the host,\nwhere consumers' click listeners live."
197
199
  },
198
200
  "getter": false,
199
201
  "setter": false,
200
- "reflect": false,
202
+ "reflect": true,
201
203
  "attribute": "disabled",
202
204
  "defaultValue": "false"
203
205
  },
@@ -2,6 +2,11 @@ import { h, } from "@stencil/core";
2
2
  import { addDays, compareYMD, getMonthMatrix, getWeekdayLabels, isDisabled, parseISO, todayYMD, } from "@prolibu-suite/cobalt-date-utils";
3
3
  import { layoutMonth, getWallClock, wallClockToEpoch, toUtcISO, zonedDateKey, zonedMinutesOfDay, } from "@prolibu-suite/cobalt-calendar-utils";
4
4
  import { readableTextToken } from "../../utils/eventColor";
5
+ // Distancia (px) que el puntero debe recorrer tras el pointerdown para que un drag
6
+ // de evento se comprometa. Por debajo de esto el gesto es un CLICK: el drag no
7
+ // arranca, el chip conserva sus pointer-events y su click abre el popover. Mismo
8
+ // literal/mecanismo que co-calendar-timegrid.tsx (DRAG_THRESHOLD_PX ahí, línea ~28).
9
+ const DRAG_THRESHOLD_PX = 5;
5
10
  /**
6
11
  * Month grid for the event calendar. Forks the day grid, roving-tabindex and
7
12
  * keyboard model from `co-calendar`, then overlays multi-day event bars laid out
@@ -38,6 +43,21 @@ export class CoCalendarMonth {
38
43
  /** Epoch ms of "now", refreshed on a 60s timer so `past` styling stays live. */
39
44
  this.nowMs = 0;
40
45
  this.onPointerUp = () => this.finishDrag();
46
+ this.onPointerMove = (e) => this.trackDrag(e);
47
+ // Drag ARMADO pero aún no comprometido: `startDrag` guarda el id y el punto de
48
+ // agarre acá; el drag pasa a activo (`dragId`) recién cuando el puntero cruza
49
+ // DRAG_THRESHOLD_PX en `trackDrag` (mismo patrón que co-calendar-timegrid.tsx,
50
+ // `dragArmedId`/`startMove`). Así un click simple en un evento arrastrable no
51
+ // arranca drag (que pone `pointer-events:none` en los chips vía
52
+ // `cal-month--interacting`) y su popover abre normal.
53
+ this.dragArmedId = null;
54
+ this.dragStartPointer = null;
55
+ // Set right before coEventDrop so the trailing click no-ops (mismo patrón que
56
+ // co-calendar-timegrid.tsx#suppressNextClick): un drag COMPROMETIDO deja el
57
+ // chip con pointer-events restaurados (`setInteracting(false)`) ANTES de que
58
+ // el navegador dispare el `click` sintético que sigue al `pointerup` — sin
59
+ // este flag ese click reabriría el popover justo después de soltar.
60
+ this.suppressNextClick = false;
41
61
  // ── Selection / activation ──────────────────────────────────────────────
42
62
  /** Día seleccionado que PERSISTE resaltado mientras el host tiene abierto su
43
63
  * form/bubble de creación (referencia visual, paridad Google). */
@@ -82,9 +102,11 @@ export class CoCalendarMonth {
82
102
  }
83
103
  connectedCallback() {
84
104
  window.addEventListener('pointerup', this.onPointerUp);
105
+ window.addEventListener('pointermove', this.onPointerMove);
85
106
  }
86
107
  disconnectedCallback() {
87
108
  window.removeEventListener('pointerup', this.onPointerUp);
109
+ window.removeEventListener('pointermove', this.onPointerMove);
88
110
  if (this.nowTimer)
89
111
  window.clearInterval(this.nowTimer);
90
112
  }
@@ -141,16 +163,43 @@ export class CoCalendarMonth {
141
163
  return;
142
164
  // Ver la nota en co-calendar-timegrid#startMove: preventDefault en
143
165
  // pointerdown mata el click y el chip arrastrable no podía activarse.
166
+ // Tampoco se toca dragId/interacting acá: eso arrancaba
167
+ // `cal-month--interacting` (pointer-events:none en los chips, css:249) en el
168
+ // pointerdown mismo y se comía el click de todo evento FUTURO arrastrable —
169
+ // el chip nunca veía el click nativo. Se arma un candidato nada más; el
170
+ // commit real ocurre en `trackDrag` al cruzar DRAG_THRESHOLD_PX.
144
171
  e.stopPropagation();
145
- this.dragId = id;
172
+ this.dragArmedId = id;
173
+ this.dragStartPointer = { x: e.clientX, y: e.clientY };
174
+ }
175
+ /** Arma → activa el drag al cruzar el umbral (ver DRAG_THRESHOLD_PX). Antes de
176
+ * eso no toca dragId/interacting, así el click nativo del chip sigue vivo. */
177
+ trackDrag(e) {
178
+ if (!this.dragArmedId || this.dragId)
179
+ return;
180
+ const sp = this.dragStartPointer;
181
+ if (!sp)
182
+ return;
183
+ if (Math.hypot(e.clientX - sp.x, e.clientY - sp.y) < DRAG_THRESHOLD_PX)
184
+ return;
185
+ this.dragId = this.dragArmedId;
186
+ this.dragArmedId = null;
146
187
  this.dragOverDate = null;
147
188
  this.setInteracting(true);
148
189
  }
149
190
  finishDrag() {
191
+ // pointerup habiendo solo ARMADO (sin cruzar el umbral) = fue un click, no un
192
+ // drag: desarmar en silencio y dejar pasar el click nativo → abre el popover.
193
+ if (this.dragArmedId && !this.dragId) {
194
+ this.dragArmedId = null;
195
+ this.dragStartPointer = null;
196
+ return;
197
+ }
150
198
  const id = this.dragId;
151
199
  const target = this.dragOverDate;
152
200
  this.dragId = null;
153
201
  this.dragOverDate = null;
202
+ this.dragStartPointer = null;
154
203
  this.setInteracting(false);
155
204
  if (!id || !target)
156
205
  return;
@@ -165,6 +214,10 @@ export class CoCalendarMonth {
165
214
  return;
166
215
  if (!this.isDayInteractable(target))
167
216
  return;
217
+ this.suppressNextClick = true;
218
+ // Clear on the next macrotask: a same-target synthetic click fires before this
219
+ // (and is suppressed); a later unrelated click is not.
220
+ setTimeout(() => { this.suppressNextClick = false; }, 0);
168
221
  const shifted = shiftEventByDays(ev, deltaDays, this.timeZone);
169
222
  this.coEventDrop.emit({
170
223
  id,
@@ -256,6 +309,10 @@ export class CoCalendarMonth {
256
309
  'cal-chip--cancelled': ev.responseState === 'cancelled' || ev.responseState === 'declined',
257
310
  }, style: style, title: ev.title, "aria-label": ev.title, onClick: (e) => {
258
311
  e.stopPropagation();
312
+ if (this.suppressNextClick) {
313
+ this.suppressNextClick = false;
314
+ return;
315
+ }
259
316
  this.activateEvent(ev.id, e.currentTarget.getBoundingClientRect());
260
317
  }, onDblClick: (e) => {
261
318
  e.stopPropagation();
@@ -269,7 +326,7 @@ export class CoCalendarMonth {
269
326
  const short = getWeekdayLabels(this.locale, this.firstDayOfWeek, 'short');
270
327
  const long = getWeekdayLabels(this.locale, this.firstDayOfWeek, 'long');
271
328
  const today = todayYMD();
272
- return (h("div", { key: 'f8bcb53b7ef7c47a1ff59caee0305dfc1a3f1fc2', class: "cal-month", part: "grid", role: "grid", onKeyDown: this.handleKeyDown, ref: (el) => (this.rootEl = el) }, h("div", { key: '11acf9973a13ad661590a04dd934c86ceaa10f5d', class: "cal-month__dow", role: "row" }, short.map((lbl, i) => (h("span", { class: "cal-month__dow-cell", role: "columnheader", "aria-label": long[i] }, lbl)))), matrix.map((week, wi) => {
329
+ return (h("div", { key: '7cb63a84f7005ee7b5552a5d9b88c598f579b7be', class: "cal-month", part: "grid", role: "grid", onKeyDown: this.handleKeyDown, ref: (el) => (this.rootEl = el) }, h("div", { key: '70281a5799b9b95957d152e15a374001a2ec8fa2', class: "cal-month__dow", role: "row" }, short.map((lbl, i) => (h("span", { class: "cal-month__dow-cell", role: "columnheader", "aria-label": long[i] }, lbl)))), matrix.map((week, wi) => {
273
330
  var _a;
274
331
  return (h("div", { class: "cal-month__week", role: "row" }, h("div", { class: "cal-month__days" }, week.map((date) => {
275
332
  const ymd = parseISO(date);
@@ -219,3 +219,25 @@
219
219
  @supports not (background: color-mix(in srgb, red, transparent)) {
220
220
  .co-checkbox:not(.co-checkbox--checked) { background: transparent; }
221
221
  }
222
+
223
+ /* Anillo de foco por teclado — solo :focus-visible, el click no lo enciende.
224
+ Clase repetida a propósito: la regla del chip overlay apagado
225
+ (.co-checkbox--overlay:not(--checked):not(--indeterminate)) pesa 0,3,0 y con un
226
+ solo .co-checkbox este bloque quedaba por debajo — el foco no se pintaba en el
227
+ estado más frecuente del overlay (WCAG 2.4.7). Con la clase duplicada empata en
228
+ 0,3,0 y gana por orden de fuente, por eso este bloque debe quedarse AL FINAL.
229
+
230
+ El halo va a --co-semantic-border-focus (50%), NO al --...-focus-ring (8%):
231
+ en los hermanos (input/select/…) el foco se percibe porque SUMAN el flip del
232
+ borde al 50% y el halo solo lo acompaña; acá el borde está anulado por los
233
+ propios estados (checked / indeterminate / overlay: `border: none`), así que
234
+ el border-color de abajo es un no-op y el halo al 8% (~1,1:1) quedaba
235
+ invisible justo en los estados más frecuentes. Con el 50% el anillo es la
236
+ única señal, y es perceptible.
237
+ NOTA: si diseño decide subir globalmente --co-semantic-border-focus-ring,
238
+ esta regla vuelve al token de halo — la decisión global sigue abierta. */
239
+ .co-checkbox.co-checkbox:focus-visible {
240
+ outline: none;
241
+ border-color: var(--co-semantic-border-focus);
242
+ box-shadow: 0 0 0 2px var(--co-semantic-border-focus);
243
+ }
@@ -30,6 +30,12 @@ export class CoCheckbox {
30
30
  }
31
31
  }
32
32
  };
33
+ this.handleKeyDown = (e) => {
34
+ if (e.key === ' ') {
35
+ e.preventDefault(); // Space no debe scrollear la página
36
+ this.handleClick(); // ya guarda disabled y resuelve indeterminate
37
+ }
38
+ };
33
39
  }
34
40
  render() {
35
41
  const isOverlay = this.appearance === 'overlay';
@@ -37,17 +43,17 @@ export class CoCheckbox {
37
43
  // Only override the fill when a custom color is provided AND the box is "on".
38
44
  // Leaving this undefined keeps the default appearance byte-identical to before.
39
45
  const boxStyle = this.onColor && isOn ? { background: this.onColor } : undefined;
40
- return (h("div", { key: '31690de633689da0b273e3c88cc200cabb4cee75', class: {
46
+ return (h("div", { key: 'caf200e3c53efa2a00d28db0e149da953e4432da', class: {
41
47
  'co-checkbox-row': true,
42
48
  'co-checkbox-row--disabled': this.disabled,
43
- }, onClick: this.handleClick }, h("span", { key: '2b0c4ebbe4c7a7a8046cf726e682a3d42ae3ebbe', role: "checkbox", "aria-checked": this.indeterminate ? 'mixed' : String(this.checked), "aria-label": this.label || '', class: {
49
+ }, onClick: this.handleClick }, h("span", { key: 'a8fe01958d81587129f304ce33f1f5fef882dd9f', role: "checkbox", "aria-checked": this.indeterminate ? 'mixed' : String(this.checked), "aria-label": this.label || '', "aria-disabled": this.disabled ? 'true' : undefined, tabIndex: this.disabled ? -1 : 0, onKeyDown: this.handleKeyDown, class: {
44
50
  'co-checkbox': true,
45
51
  [`co-checkbox--${this.size}`]: true,
46
52
  'co-checkbox--overlay': isOverlay,
47
53
  'co-checkbox--checked': this.checked && !this.indeterminate,
48
54
  'co-checkbox--indeterminate': this.indeterminate,
49
55
  'co-checkbox--disabled': this.disabled,
50
- }, style: boxStyle, part: "box" }, h("svg", { key: 'a127c176e0233460f14c4261d8d2157e657689a9', class: "co-checkbox__glyph", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" }, h("path", { key: 'ad7dc0018cdae31156884cd04326bbcdba132b4f', class: "co-checkbox__check", d: "M3.5 8.5l3 3 6-6.5", pathLength: "1", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { key: 'dd665144b25aa51339cf9f6ce0e0edf65cfbe5a9', class: "co-checkbox__dash", d: "M3 8h10", pathLength: "1", "stroke-linecap": "round" }))), this.label && (h("span", { key: '08d6c695e2cbb8188564dc8bb5aab9b569c24838', class: "co-checkbox__label", part: "label" }, this.label))));
56
+ }, style: boxStyle, part: "box" }, h("svg", { key: 'd9ea60be2c2e58c227aeca8ae3b8e8f459f3522d', class: "co-checkbox__glyph", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" }, h("path", { key: 'd25f835b95a2200d44b5e4afdeb727d758e0650f', class: "co-checkbox__check", d: "M3.5 8.5l3 3 6-6.5", pathLength: "1", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { key: 'b7a043b9910736a8bdd15720fd7857c01250086d', class: "co-checkbox__dash", d: "M3 8h10", pathLength: "1", "stroke-linecap": "round" }))), this.label && (h("span", { key: '2e256fb7fd1515bf9dbe3dc380a0d7d09d5347ac', class: "co-checkbox__label", part: "label" }, this.label))));
51
57
  }
52
58
  static get is() { return "co-checkbox"; }
53
59
  static get encapsulation() { return "shadow"; }
@@ -231,7 +231,7 @@ export class CoCheckboxGroup {
231
231
  "references": {
232
232
  "CheckboxOption": {
233
233
  "location": "local",
234
- "path": "/Users/agox/worktrees/cobalt-release-093/packages/core/src/components/co-checkbox-group/co-checkbox-group.tsx",
234
+ "path": "/Users/agox/Documents/Prolibu/prolibu-cobalt/packages/core/src/components/co-checkbox-group/co-checkbox-group.tsx",
235
235
  "id": "src/components/co-checkbox-group/co-checkbox-group.tsx::CheckboxOption"
236
236
  }
237
237
  }
@@ -100,6 +100,13 @@
100
100
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--co-semantic-border-focus) 15%, transparent);
101
101
  }
102
102
 
103
+ /* Anillo de foco por teclado — mismo trazo que el estado abierto. */
104
+ .co-color-picker__trigger:focus-visible {
105
+ outline: none;
106
+ border-color: var(--co-semantic-border-focus);
107
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--co-semantic-border-focus) 15%, transparent);
108
+ }
109
+
103
110
  .co-color-picker--error .co-color-picker__trigger {
104
111
  border-color: var(--co-color-status-error);
105
112
  }
@@ -38,6 +38,19 @@ export class CoColorPicker {
38
38
  };
39
39
  this.handlePopoverClose = () => {
40
40
  this.isOpen = false;
41
+ this.restoreTriggerFocus();
42
+ };
43
+ this.handleTriggerKeyDown = (e) => {
44
+ if (this.disabled)
45
+ return;
46
+ if (e.key === 'Enter' || e.key === ' ') {
47
+ e.preventDefault();
48
+ this.isOpen = !this.isOpen;
49
+ }
50
+ else if (e.key === 'ArrowDown' && !this.isOpen) {
51
+ e.preventDefault();
52
+ this.isOpen = true;
53
+ }
41
54
  };
42
55
  }
43
56
  get parsedPresets() {
@@ -55,10 +68,21 @@ export class CoColorPicker {
55
68
  }
56
69
  return DEFAULT_PRESETS;
57
70
  }
71
+ /** Devuelve el foco al trigger al cerrar el panel (Escape, selección),
72
+ * salvo que el cierre venga de un click-outside que ya movió el foco a
73
+ * otro elemento — en ese caso no robarlo. */
74
+ restoreTriggerFocus() {
75
+ var _a;
76
+ const active = document.activeElement;
77
+ if (active === this.el || this.el.contains(active)) {
78
+ (_a = this.triggerEl) === null || _a === void 0 ? void 0 : _a.focus();
79
+ }
80
+ }
58
81
  handleSwatchClick(hex) {
59
82
  this.value = hex;
60
83
  this.coChange.emit(hex);
61
84
  this.isOpen = false;
85
+ this.restoreTriggerFocus();
62
86
  }
63
87
  get statusIcon() {
64
88
  const map = {
@@ -82,10 +106,10 @@ export class CoColorPicker {
82
106
  const presets = this.parsedPresets;
83
107
  const hasValue = !!this.value;
84
108
  const statusClass = this.status !== 'default' ? `co-color-picker--${this.status}` : '';
85
- return (h("div", { key: '41a7a80f81aeaf71e1f8429e66aa5511437882fa', class: `co-color-picker co-color-picker--${this.size} ${statusClass} ${this.isOpen ? 'co-color-picker--open' : ''} ${this.disabled ? 'co-color-picker--disabled' : ''}` }, this.label && (h("label", { key: '96404f15d058b7bc0b2d2c13b6744404cc11769a', class: "co-color-picker__label" }, this.label, h(RequiredMark, { key: '16af407f56b58ad6ea649a19aa8a3c96f2caae04', required: this.required }))), h("co-popover", { key: '8eb3b862b8618b32d76e862ce003e667801c3262', open: this.isOpen, placement: "bottom-start", matchWidth: true, offset: 4, onCoOpen: this.handlePopoverOpen, onCoClose: this.handlePopoverClose }, h("div", { key: '0314b0dbf7404fdb23a0b5cd837b07b5879f85a1', slot: "trigger", class: "co-color-picker__trigger" }, h("span", { key: '0d08254d17a15786834c5de548526b6b359a09be', class: "co-color-picker__swatch", style: { background: hasValue ? this.value : 'transparent' } }), h("span", { key: 'afbfa48cb5bd140767acdf2e1df35d7e8985948d', class: `co-color-picker__value ${!hasValue ? 'co-color-picker__value--empty' : ''}` }, hasValue ? this.value : this.placeholder)), this.isOpen && (h("div", { key: '9e0d22b668546a175c6e6559c2af5dd0de2a1843', class: "co-color-picker__panel" }, h("div", { key: '018fec91282680d660be82b01f340b29f13c73f7', class: "co-color-picker__grid" }, presets.map((preset) => {
109
+ return (h("div", { key: '5b99c6a39399a6731d09115e7fd2ce2d64baffb2', class: `co-color-picker co-color-picker--${this.size} ${statusClass} ${this.isOpen ? 'co-color-picker--open' : ''} ${this.disabled ? 'co-color-picker--disabled' : ''}` }, this.label && (h("label", { key: '816392c0b3cebcb2e84ea28ea7e6b7976503b6f9', class: "co-color-picker__label" }, this.label, h(RequiredMark, { key: 'e0e1c97522f4516763fc663878203e2dafb783b3', required: this.required }))), h("co-popover", { key: 'd49ba1f9cd64d2498940247f3ba02897dcc9d2d7', open: this.isOpen, placement: "bottom-start", matchWidth: true, offset: 4, onCoOpen: this.handlePopoverOpen, onCoClose: this.handlePopoverClose }, h("div", { key: '1121b429c89fb9cc066cc66564ae547687bf8ed3', slot: "trigger", class: "co-color-picker__trigger", role: "combobox", "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "dialog", "aria-controls": this.isOpen ? CoColorPicker.PANEL_ID : undefined, "aria-label": this.label || undefined, "aria-disabled": this.disabled ? 'true' : undefined, tabIndex: this.disabled ? -1 : 0, ref: (el) => (this.triggerEl = el), onKeyDown: this.handleTriggerKeyDown }, h("span", { key: '2567db35022b5ffdd9921d8bec263c90103fd5f3', class: "co-color-picker__swatch", style: { background: hasValue ? this.value : 'transparent' } }), h("span", { key: '7fecc93b5ecc91acf2c874a26ed1f9d8770d9adf', class: `co-color-picker__value ${!hasValue ? 'co-color-picker__value--empty' : ''}` }, hasValue ? this.value : this.placeholder)), this.isOpen && (h("div", { key: '44d904e4156fac15b302ffe86d68cef72bc7518e', class: "co-color-picker__panel", id: CoColorPicker.PANEL_ID, role: "dialog", "aria-label": this.label || this.placeholder }, h("div", { key: 'a566f189c87792f0af3e493c00eed7240dcd49c9', class: "co-color-picker__grid" }, presets.map((preset) => {
86
110
  var _a;
87
111
  return (h("button", { type: "button", key: preset.value, class: `co-color-picker__grid-item ${preset.value.toUpperCase() === ((_a = this.value) === null || _a === void 0 ? void 0 : _a.toUpperCase()) ? 'co-color-picker__grid-item--selected' : ''}`, style: { background: preset.value }, title: preset.name, onClick: () => this.handleSwatchClick(preset.value) }));
88
- })), hasValue && (h("div", { key: '83420abbf343f57fa57804c676fd22e91d2f6816', class: "co-color-picker__hex-row" }, h("span", { key: '1b9ce2cdb091734c9d34003c8ce48a165e8763a8', class: "co-color-picker__hex-prefix" }, "#"), h("span", { key: '5733ec9110227e3ffa45c15c294629cfe420c485', class: "co-color-picker__hex-value" }, this.value.replace(/^#/, '')), h("button", { key: '249b0f7eb80517c89ecaa11c09cf64ae70fd3d52', type: "button", class: `co-color-picker__copy-btn ${this.copied ? 'co-color-picker__copy-btn--copied' : ''}`, title: "Copy", onClick: () => this.handleCopy() }, h("co-icon", { key: 'aed9a2fe68c39047e6027c76c185a1cfc5abb097', name: this.copied ? 'check' : 'copy', size: 16 }))))))), this.helperText && (h("div", { key: '276d4f39ca9f5be6add8bd2ae423a02960f82581', class: "co-color-picker__helper" }, this.statusIcon && (h("co-icon", { key: '4f4323dc9f3d26581fb6ec1f5bd1e668e9a45011', name: this.statusIcon, size: 14, weight: "fill", class: "co-color-picker__helper-icon" })), h("span", { key: 'ee725d0ce3a30985a71bf2d34e5e64124f89891b', class: "co-color-picker__helper-text" }, this.helperText)))));
112
+ })), hasValue && (h("div", { key: 'bae8ffe387fe30710b654975379e70a4b3724ec9', class: "co-color-picker__hex-row" }, h("span", { key: '671a5a64abc2f824c8d1ef8c2404023d8d51c28d', class: "co-color-picker__hex-prefix" }, "#"), h("span", { key: '3a6742eb528c5a92fe16b2f2e5d8b2ebdf885450', class: "co-color-picker__hex-value" }, this.value.replace(/^#/, '')), h("button", { key: 'f53957b494dbed0648d32fcb743673a2ada063b4', type: "button", class: `co-color-picker__copy-btn ${this.copied ? 'co-color-picker__copy-btn--copied' : ''}`, title: "Copy", onClick: () => this.handleCopy() }, h("co-icon", { key: 'c99e611beddc2108f3973ecee4f6012423cffd23', name: this.copied ? 'check' : 'copy', size: 16 }))))))), this.helperText && (h("div", { key: '1d6a6e97eab1eafcb9265944693cb0d1121e04ee', class: "co-color-picker__helper" }, this.statusIcon && (h("co-icon", { key: 'bcf943836fddf264cede2f339844f35aa167002f', name: this.statusIcon, size: 14, weight: "fill", class: "co-color-picker__helper-icon" })), h("span", { key: 'a439c4d102d17d0d6c9e55757dcf43ec8b3ade8f', class: "co-color-picker__helper-text" }, this.helperText)))));
89
113
  }
90
114
  static get is() { return "co-color-picker"; }
91
115
  static get encapsulation() { return "shadow"; }
@@ -353,3 +377,12 @@ export class CoColorPicker {
353
377
  }
354
378
  static get elementRef() { return "el"; }
355
379
  }
380
+ /**
381
+ * Id del panel de presets, target de `aria-controls` del trigger. Constante
382
+ * (no aleatoria): vive en el shadow root de ESTA instancia, donde los ids no
383
+ * colisionan con los de otras.
384
+ *
385
+ * El trigger solo emite `aria-controls` mientras `isOpen`: el panel se monta
386
+ * on-demand, así que cerrado el IDREF quedaría colgante.
387
+ */
388
+ CoColorPicker.PANEL_ID = 'co-color-picker-panel';