@unicitylabs/sphere-ui 0.1.7 → 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 +80 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -474,7 +474,8 @@ function EmptyState({ title, description, action }) {
474
474
  }
475
475
 
476
476
  // src/components/CustomSelect.tsx
477
- 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";
478
479
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
479
480
  function CustomSelect({
480
481
  options,
@@ -485,13 +486,23 @@ function CustomSelect({
485
486
  size = "md"
486
487
  }) {
487
488
  const [open, setOpen] = useState2(false);
488
- 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 });
489
492
  const selected = options.find((o) => o.value === value);
490
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
+ }, []);
491
499
  useEffect3(() => {
492
500
  if (!open) return;
493
501
  const handler = (e) => {
494
- 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);
495
506
  };
496
507
  document.addEventListener("mousedown", handler);
497
508
  return () => document.removeEventListener("mousedown", handler);
@@ -504,13 +515,27 @@ function CustomSelect({
504
515
  document.addEventListener("keydown", handler);
505
516
  return () => document.removeEventListener("keydown", handler);
506
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]);
507
528
  const textSize = size === "sm" ? "text-xs" : "text-sm";
508
- return /* @__PURE__ */ jsxs10("div", { ref, className: `relative ${className}`, children: [
529
+ return /* @__PURE__ */ jsxs10("div", { className, children: [
509
530
  /* @__PURE__ */ jsxs10(
510
531
  "button",
511
532
  {
533
+ ref: btnRef,
512
534
  type: "button",
513
- onClick: () => setOpen((o) => !o),
535
+ onClick: () => {
536
+ updatePos();
537
+ setOpen((o) => !o);
538
+ },
514
539
  className: `admin-input w-full flex items-center justify-between gap-2 ${textSize} text-left`,
515
540
  style: { color: selected ? "var(--text-primary)" : "var(--text-muted)" },
516
541
  children: [
@@ -537,40 +562,50 @@ function CustomSelect({
537
562
  ]
538
563
  }
539
564
  ),
540
- open && /* @__PURE__ */ jsx11(
541
- "div",
542
- {
543
- className: "absolute left-0 right-0 top-full mt-1 z-30 py-1 max-h-48 overflow-y-auto",
544
- style: {
545
- background: "var(--bg-elevated)",
546
- border: "1px solid var(--border)",
547
- borderRadius: "var(--radius-md)",
548
- boxShadow: "0 8px 24px rgba(0,0,0,0.4)"
549
- },
550
- children: options.map((opt) => /* @__PURE__ */ jsx11(
551
- "button",
552
- {
553
- type: "button",
554
- onClick: () => {
555
- onChange(opt.value);
556
- setOpen(false);
557
- },
558
- className: `block w-full text-left px-3 py-1.5 ${textSize} transition-colors`,
559
- style: {
560
- color: opt.value === value ? "var(--accent-text)" : "var(--text-primary)",
561
- background: opt.value === value ? "var(--accent-glow)" : "transparent"
562
- },
563
- onMouseEnter: (e) => {
564
- if (opt.value !== value) e.currentTarget.style.background = "var(--bg-hover)";
565
- },
566
- onMouseLeave: (e) => {
567
- e.currentTarget.style.background = opt.value === value ? "var(--accent-glow)" : "transparent";
568
- },
569
- 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)"
570
582
  },
571
- opt.value
572
- ))
573
- }
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
574
609
  )
575
610
  ] });
576
611
  }
@@ -703,7 +738,7 @@ function AlertBanner({ type, title, children }) {
703
738
  }
704
739
 
705
740
  // src/components/AddressDisplay.tsx
706
- import { useState as useState4, useCallback } from "react";
741
+ import { useState as useState4, useCallback as useCallback2 } from "react";
707
742
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
708
743
  function truncateAddress(address) {
709
744
  const prefix = "DIRECT://";
@@ -720,7 +755,7 @@ function truncateAddress(address) {
720
755
  }
721
756
  function AddressDisplay({ address, nametag, truncate = true }) {
722
757
  const [copied, setCopied] = useState4(false);
723
- const handleCopy = useCallback(() => {
758
+ const handleCopy = useCallback2(() => {
724
759
  navigator.clipboard.writeText(address).then(() => {
725
760
  setCopied(true);
726
761
  setTimeout(() => setCopied(false), 1500);
@@ -774,7 +809,7 @@ function AddressDisplay({ address, nametag, truncate = true }) {
774
809
  }
775
810
 
776
811
  // src/components/JsonPanel.tsx
777
- 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";
778
813
  import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
779
814
  function JsonPanel({
780
815
  value,
@@ -796,7 +831,7 @@ function JsonPanel({
796
831
  setText(JSON.stringify(clean, null, 2));
797
832
  setParseError(null);
798
833
  }, [value, isEditing, excludeKeys]);
799
- const handleChange = useCallback2((newText) => {
834
+ const handleChange = useCallback3((newText) => {
800
835
  setText(newText);
801
836
  try {
802
837
  const parsed = JSON.parse(newText);
@@ -806,10 +841,10 @@ function JsonPanel({
806
841
  setParseError(e instanceof Error ? e.message : "Invalid JSON");
807
842
  }
808
843
  }, [onChange]);
809
- const handleFocus = useCallback2(() => setIsEditing(true), []);
810
- const handleBlur = useCallback2(() => setIsEditing(false), []);
844
+ const handleFocus = useCallback3(() => setIsEditing(true), []);
845
+ const handleBlur = useCallback3(() => setIsEditing(false), []);
811
846
  const [copied, setCopied] = useState5(false);
812
- const handleCopy = useCallback2(async () => {
847
+ const handleCopy = useCallback3(async () => {
813
848
  await navigator.clipboard.writeText(text);
814
849
  setCopied(true);
815
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.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",