@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 +1 -1
- package/src/actionbar/ActionBar.tsx +38 -36
package/package.json
CHANGED
|
@@ -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 (
|