@unicitylabs/sphere-ui 0.1.6 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +87 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20,6 +20,11 @@ function DashboardLayout({ logo, nav, footer, children }) {
20
20
  background: "var(--bg-root)",
21
21
  borderRight: "1px solid var(--border)"
22
22
  },
23
+ onClick: (e) => {
24
+ if (e.target.closest("button, a")) {
25
+ setMobileOpen(false);
26
+ }
27
+ },
23
28
  children: [
24
29
  /* @__PURE__ */ jsx(
25
30
  "div",
@@ -49,9 +54,8 @@ function DashboardLayout({ logo, nav, footer, children }) {
49
54
  ),
50
55
  logo
51
56
  ] }),
52
- /* @__PURE__ */ jsx("nav", { className: "flex-1 px-3 py-3 flex flex-col overflow-y-auto", onClick: () => setMobileOpen(false), children: nav }),
53
- footer && /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
54
- /* @__PURE__ */ jsx("div", { className: "px-3 py-4", style: { borderTop: "1px solid var(--border)" }, onClick: () => setMobileOpen(false), children: footer })
57
+ /* @__PURE__ */ jsx("nav", { className: "flex-1 px-3 py-3 flex flex-col overflow-y-auto", children: nav }),
58
+ footer && /* @__PURE__ */ jsx("div", { className: "px-3 py-4", style: { borderTop: "1px solid var(--border)" }, children: footer })
55
59
  ]
56
60
  }
57
61
  ),
@@ -470,7 +474,8 @@ function EmptyState({ title, description, action }) {
470
474
  }
471
475
 
472
476
  // src/components/CustomSelect.tsx
473
- import { useState as useState2, useRef, useEffect as useEffect3 } from "react";
477
+ import { useState as useState2, useRef, useEffect as useEffect3, useCallback } from "react";
478
+ import { createPortal as createPortal3 } from "react-dom";
474
479
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
475
480
  function CustomSelect({
476
481
  options,
@@ -481,13 +486,23 @@ function CustomSelect({
481
486
  size = "md"
482
487
  }) {
483
488
  const [open, setOpen] = useState2(false);
484
- const ref = useRef(null);
489
+ const btnRef = useRef(null);
490
+ const dropRef = useRef(null);
491
+ const [pos, setPos] = useState2({ top: 0, left: 0, width: 0 });
485
492
  const selected = options.find((o) => o.value === value);
486
493
  const label = selected?.label ?? placeholder ?? "Select...";
494
+ const updatePos = useCallback(() => {
495
+ if (!btnRef.current) return;
496
+ const rect = btnRef.current.getBoundingClientRect();
497
+ setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width });
498
+ }, []);
487
499
  useEffect3(() => {
488
500
  if (!open) return;
489
501
  const handler = (e) => {
490
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
502
+ const target = e.target;
503
+ if (btnRef.current?.contains(target)) return;
504
+ if (dropRef.current?.contains(target)) return;
505
+ setOpen(false);
491
506
  };
492
507
  document.addEventListener("mousedown", handler);
493
508
  return () => document.removeEventListener("mousedown", handler);
@@ -500,13 +515,27 @@ function CustomSelect({
500
515
  document.addEventListener("keydown", handler);
501
516
  return () => document.removeEventListener("keydown", handler);
502
517
  }, [open]);
518
+ useEffect3(() => {
519
+ if (!open) return;
520
+ updatePos();
521
+ window.addEventListener("scroll", updatePos, true);
522
+ window.addEventListener("resize", updatePos);
523
+ return () => {
524
+ window.removeEventListener("scroll", updatePos, true);
525
+ window.removeEventListener("resize", updatePos);
526
+ };
527
+ }, [open, updatePos]);
503
528
  const textSize = size === "sm" ? "text-xs" : "text-sm";
504
- return /* @__PURE__ */ jsxs10("div", { ref, className: `relative ${className}`, children: [
529
+ return /* @__PURE__ */ jsxs10("div", { className, children: [
505
530
  /* @__PURE__ */ jsxs10(
506
531
  "button",
507
532
  {
533
+ ref: btnRef,
508
534
  type: "button",
509
- onClick: () => setOpen((o) => !o),
535
+ onClick: () => {
536
+ updatePos();
537
+ setOpen((o) => !o);
538
+ },
510
539
  className: `admin-input w-full flex items-center justify-between gap-2 ${textSize} text-left`,
511
540
  style: { color: selected ? "var(--text-primary)" : "var(--text-muted)" },
512
541
  children: [
@@ -533,40 +562,50 @@ function CustomSelect({
533
562
  ]
534
563
  }
535
564
  ),
536
- open && /* @__PURE__ */ jsx11(
537
- "div",
538
- {
539
- className: "absolute left-0 right-0 top-full mt-1 z-30 py-1 max-h-48 overflow-y-auto",
540
- style: {
541
- background: "var(--bg-elevated)",
542
- border: "1px solid var(--border)",
543
- borderRadius: "var(--radius-md)",
544
- boxShadow: "0 8px 24px rgba(0,0,0,0.4)"
545
- },
546
- children: options.map((opt) => /* @__PURE__ */ jsx11(
547
- "button",
548
- {
549
- type: "button",
550
- onClick: () => {
551
- onChange(opt.value);
552
- setOpen(false);
553
- },
554
- className: `block w-full text-left px-3 py-1.5 ${textSize} transition-colors`,
555
- style: {
556
- color: opt.value === value ? "var(--accent-text)" : "var(--text-primary)",
557
- background: opt.value === value ? "var(--accent-glow)" : "transparent"
558
- },
559
- onMouseEnter: (e) => {
560
- if (opt.value !== value) e.currentTarget.style.background = "var(--bg-hover)";
561
- },
562
- onMouseLeave: (e) => {
563
- e.currentTarget.style.background = opt.value === value ? "var(--accent-glow)" : "transparent";
564
- },
565
- children: opt.label
565
+ open && createPortal3(
566
+ /* @__PURE__ */ jsx11(
567
+ "div",
568
+ {
569
+ ref: dropRef,
570
+ className: "py-1 max-h-48 overflow-y-auto",
571
+ style: {
572
+ position: "fixed",
573
+ top: pos.top,
574
+ left: pos.left,
575
+ width: pos.width,
576
+ minWidth: 120,
577
+ zIndex: 9999,
578
+ background: "var(--bg-elevated)",
579
+ border: "1px solid var(--border)",
580
+ borderRadius: "var(--radius-md)",
581
+ boxShadow: "0 8px 24px rgba(0,0,0,0.4)"
566
582
  },
567
- opt.value
568
- ))
569
- }
583
+ children: options.map((opt) => /* @__PURE__ */ jsx11(
584
+ "button",
585
+ {
586
+ type: "button",
587
+ onClick: () => {
588
+ onChange(opt.value);
589
+ setOpen(false);
590
+ },
591
+ className: `block w-full text-left px-3 py-1.5 ${textSize} transition-colors`,
592
+ style: {
593
+ color: opt.value === value ? "var(--accent-text)" : "var(--text-primary)",
594
+ background: opt.value === value ? "var(--accent-glow)" : "transparent"
595
+ },
596
+ onMouseEnter: (e) => {
597
+ if (opt.value !== value) e.currentTarget.style.background = "var(--bg-hover)";
598
+ },
599
+ onMouseLeave: (e) => {
600
+ e.currentTarget.style.background = opt.value === value ? "var(--accent-glow)" : "transparent";
601
+ },
602
+ children: opt.label
603
+ },
604
+ opt.value
605
+ ))
606
+ }
607
+ ),
608
+ document.body
570
609
  )
571
610
  ] });
572
611
  }
@@ -699,7 +738,7 @@ function AlertBanner({ type, title, children }) {
699
738
  }
700
739
 
701
740
  // src/components/AddressDisplay.tsx
702
- import { useState as useState4, useCallback } from "react";
741
+ import { useState as useState4, useCallback as useCallback2 } from "react";
703
742
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
704
743
  function truncateAddress(address) {
705
744
  const prefix = "DIRECT://";
@@ -716,7 +755,7 @@ function truncateAddress(address) {
716
755
  }
717
756
  function AddressDisplay({ address, nametag, truncate = true }) {
718
757
  const [copied, setCopied] = useState4(false);
719
- const handleCopy = useCallback(() => {
758
+ const handleCopy = useCallback2(() => {
720
759
  navigator.clipboard.writeText(address).then(() => {
721
760
  setCopied(true);
722
761
  setTimeout(() => setCopied(false), 1500);
@@ -770,7 +809,7 @@ function AddressDisplay({ address, nametag, truncate = true }) {
770
809
  }
771
810
 
772
811
  // src/components/JsonPanel.tsx
773
- import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback2 } from "react";
812
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback3 } from "react";
774
813
  import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
775
814
  function JsonPanel({
776
815
  value,
@@ -792,7 +831,7 @@ function JsonPanel({
792
831
  setText(JSON.stringify(clean, null, 2));
793
832
  setParseError(null);
794
833
  }, [value, isEditing, excludeKeys]);
795
- const handleChange = useCallback2((newText) => {
834
+ const handleChange = useCallback3((newText) => {
796
835
  setText(newText);
797
836
  try {
798
837
  const parsed = JSON.parse(newText);
@@ -802,10 +841,10 @@ function JsonPanel({
802
841
  setParseError(e instanceof Error ? e.message : "Invalid JSON");
803
842
  }
804
843
  }, [onChange]);
805
- const handleFocus = useCallback2(() => setIsEditing(true), []);
806
- const handleBlur = useCallback2(() => setIsEditing(false), []);
844
+ const handleFocus = useCallback3(() => setIsEditing(true), []);
845
+ const handleBlur = useCallback3(() => setIsEditing(false), []);
807
846
  const [copied, setCopied] = useState5(false);
808
- const handleCopy = useCallback2(async () => {
847
+ const handleCopy = useCallback3(async () => {
809
848
  await navigator.clipboard.writeText(text);
810
849
  setCopied(true);
811
850
  setTimeout(() => setCopied(false), 1500);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unicitylabs/sphere-ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",