@cosxai/ui 0.17.0 → 0.17.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosxai/ui",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "COSX design system \u2014 React 19 component primitives shared across product-meta and other consumers",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -332,6 +332,44 @@ export function ActionBar({
332
332
  return () => window.removeEventListener("keydown", onKey);
333
333
  }, [expandedKey, setExpandedKey]);
334
334
 
335
+ // ----- Panel slot (design#13): outside-click + Escape close. -----
336
+ // Hooks live ABOVE the early returns below — hook order must not
337
+ // change when the bar switches between empty/collapsed/full.
338
+ const panelOpen = Boolean(panel?.open);
339
+ const panelClose = panel?.onOpenChange;
340
+ useEffect(() => {
341
+ if (!panelOpen || !panelClose) return;
342
+ const onDown = (e: MouseEvent) => {
343
+ const t = e.target as Node | null;
344
+ if (t && barRef.current?.contains(t)) return;
345
+ panelClose(false);
346
+ };
347
+ const onKey = (e: KeyboardEvent) => {
348
+ if (e.key === "Escape") panelClose(false);
349
+ };
350
+ window.addEventListener("mousedown", onDown);
351
+ window.addEventListener("keydown", onKey);
352
+ return () => {
353
+ window.removeEventListener("mousedown", onDown);
354
+ window.removeEventListener("keydown", onKey);
355
+ };
356
+ }, [panelOpen, panelClose]);
357
+
358
+ // Horizontal clamp: centred on the bar unless that would push the
359
+ // panel past a viewport edge — then shift just enough to fit.
360
+ const panelRef = useRef<HTMLDivElement | null>(null);
361
+ useLayoutEffect(() => {
362
+ if (!panelOpen) return;
363
+ const el = panelRef.current;
364
+ if (!el) return;
365
+ el.style.marginLeft = "0px";
366
+ const rect = el.getBoundingClientRect();
367
+ let shift = 0;
368
+ if (rect.left < 8) shift = 8 - rect.left;
369
+ else if (rect.right > window.innerWidth - 8) shift = window.innerWidth - 8 - rect.right;
370
+ if (shift !== 0) el.style.marginLeft = `${Math.round(shift)}px`;
371
+ }, [panelOpen, pos]);
372
+
335
373
  // ----- Empty state -----
336
374
  // Bar renders when EITHER any page item is registered OR the
337
375
  // status dot is set. Without the statusDot check, an app whose
@@ -383,42 +421,6 @@ export function ActionBar({
383
421
  );
384
422
  }
385
423
 
386
- // ----- Panel slot (design#13): outside-click + Escape close. -----
387
- const panelOpen = Boolean(panel?.open);
388
- const panelClose = panel?.onOpenChange;
389
- useEffect(() => {
390
- if (!panelOpen || !panelClose) return;
391
- const onDown = (e: MouseEvent) => {
392
- const t = e.target as Node | null;
393
- if (t && barRef.current?.contains(t)) return;
394
- panelClose(false);
395
- };
396
- const onKey = (e: KeyboardEvent) => {
397
- if (e.key === "Escape") panelClose(false);
398
- };
399
- window.addEventListener("mousedown", onDown);
400
- window.addEventListener("keydown", onKey);
401
- return () => {
402
- window.removeEventListener("mousedown", onDown);
403
- window.removeEventListener("keydown", onKey);
404
- };
405
- }, [panelOpen, panelClose]);
406
-
407
- // Horizontal clamp: centred on the bar unless that would push the
408
- // panel past a viewport edge — then shift just enough to fit.
409
- const panelRef = useRef<HTMLDivElement | null>(null);
410
- useLayoutEffect(() => {
411
- if (!panelOpen) return;
412
- const el = panelRef.current;
413
- if (!el) return;
414
- el.style.marginLeft = "0px";
415
- const rect = el.getBoundingClientRect();
416
- let shift = 0;
417
- if (rect.left < 8) shift = 8 - rect.left;
418
- else if (rect.right > window.innerWidth - 8) shift = window.innerWidth - 8 - rect.right;
419
- if (shift !== 0) el.style.marginLeft = `${Math.round(shift)}px`;
420
- }, [panelOpen, pos]);
421
-
422
424
  // ----- Full bar -----
423
425
  const isDefault = pos.type === "default";
424
426
  return (