@cosxai/ui 0.2.6 → 0.2.8

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosxai/ui",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "COSX design system — React 19 component primitives shared across product-meta and other consumers",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -70,7 +70,7 @@ export function ActionBarButton({
70
70
  ...style,
71
71
  }}
72
72
  >
73
- {icon}
73
+ {icon != null && <span className="ck-actionbar-icon">{icon}</span>}
74
74
  <span className={responsiveLabel ? "ck-actionbar-label" : undefined}>
75
75
  {label}
76
76
  </span>
@@ -37,9 +37,10 @@ import { cn } from "../lib/cn";
37
37
  // Popover ALWAYS renders via createPortal to document.body so it
38
38
  // escapes Card / Drawer / Dialog parents whose overflow:hidden
39
39
  // would otherwise clip it. Position is computed against the
40
- // trigger's bounding rect and recomputed on resize; we
41
- // intentionally close on scroll to avoid the "popover drifts off
42
- // the trigger" failure mode (matches Radix / Headless UI default).
40
+ // trigger's bounding rect and re-computed on page scroll + window
41
+ // resize so the popover tracks the trigger. Scrolls INSIDE the
42
+ // popover (the option list / search input) are filtered out — only
43
+ // outer scrolls reposition.
43
44
  //
44
45
  // Keyboard model (mirrors ARIA 1.2 combobox-as-listbox spec):
45
46
  // trigger CLOSED:
@@ -208,8 +209,19 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(function Select
208
209
 
209
210
  useEffect(() => {
210
211
  if (!open) return;
211
- const onResize = () => setRect(computeRect());
212
- const onScroll = () => setOpen(false);
212
+ const reposition = () => {
213
+ const r = computeRect();
214
+ if (r) setRect(r);
215
+ };
216
+ const onResize = reposition;
217
+ const onScroll = (e: Event) => {
218
+ // Scrolls INSIDE the popover (the option list / search input)
219
+ // must not re-trigger positioning — they're not page scrolls.
220
+ // capture=true catches them before they bubble; we filter here.
221
+ const target = e.target as Node | null;
222
+ if (target && popoverRef.current?.contains(target)) return;
223
+ reposition();
224
+ };
213
225
  window.addEventListener("resize", onResize);
214
226
  window.addEventListener("scroll", onScroll, true);
215
227
  return () => {
@@ -172,6 +172,22 @@ html[data-ck-chrome="seamless"] .ck-card__foot {
172
172
  cursor: not-allowed;
173
173
  }
174
174
 
175
+ /* Icon glyph wrapper — sized larger than the button's 13 px label
176
+ text so unicode symbols (◐ ☀ ☾ ◇) and small SVGs read clearly.
177
+ ActionBarButton wraps the icon prop in this span automatically;
178
+ callers don't need to opt in. The 1 px upward translate
179
+ compensates for geometric glyphs sitting about 1 px below their
180
+ em-box centre — flex centring otherwise leaves them looking
181
+ slightly low next to the heavier label text. */
182
+ .ck-actionbar-icon {
183
+ display: inline-flex;
184
+ align-items: center;
185
+ justify-content: center;
186
+ font-size: 16px;
187
+ line-height: 1;
188
+ transform: translateY(-1px);
189
+ }
190
+
175
191
  /* Hide labels below 768 px (the standard md breakpoint).
176
192
  Icons + tooltips remain — the bar's still informative on phones. */
177
193
  @media (max-width: 767px) {