@unicitylabs/sphere-ui 0.1.7 → 0.1.9

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 -46
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -396,13 +396,19 @@ function ConfirmDialog({
396
396
  // src/components/StatusBadge.tsx
397
397
  import { jsx as jsx8 } from "react/jsx-runtime";
398
398
  var STATUS_COLORS = {
399
+ // Quest/Achievement statuses (uppercase)
399
400
  ACTIVE: "badge-green",
400
401
  DRAFT: "badge-gray",
401
402
  PAUSED: "badge-yellow",
402
403
  EXPIRED: "badge-red",
403
404
  ENDED: "badge-red",
404
405
  AWARDED: "badge-green",
405
- REJECTED: "badge-red"
406
+ REJECTED: "badge-red",
407
+ // Project statuses (lowercase)
408
+ draft: "badge-gray",
409
+ review: "badge-yellow",
410
+ published: "badge-green",
411
+ suspended: "badge-red"
406
412
  };
407
413
  function StatusBadge({ status, className = "" }) {
408
414
  return /* @__PURE__ */ jsx8("span", { className: `badge ${STATUS_COLORS[status] ?? "badge-gray"} ${className}`, children: status });
@@ -474,7 +480,8 @@ function EmptyState({ title, description, action }) {
474
480
  }
475
481
 
476
482
  // src/components/CustomSelect.tsx
477
- import { useState as useState2, useRef, useEffect as useEffect3 } from "react";
483
+ import { useState as useState2, useRef, useEffect as useEffect3, useCallback } from "react";
484
+ import { createPortal as createPortal3 } from "react-dom";
478
485
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
479
486
  function CustomSelect({
480
487
  options,
@@ -485,13 +492,23 @@ function CustomSelect({
485
492
  size = "md"
486
493
  }) {
487
494
  const [open, setOpen] = useState2(false);
488
- const ref = useRef(null);
495
+ const btnRef = useRef(null);
496
+ const dropRef = useRef(null);
497
+ const [pos, setPos] = useState2({ top: 0, left: 0, width: 0 });
489
498
  const selected = options.find((o) => o.value === value);
490
499
  const label = selected?.label ?? placeholder ?? "Select...";
500
+ const updatePos = useCallback(() => {
501
+ if (!btnRef.current) return;
502
+ const rect = btnRef.current.getBoundingClientRect();
503
+ setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width });
504
+ }, []);
491
505
  useEffect3(() => {
492
506
  if (!open) return;
493
507
  const handler = (e) => {
494
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
508
+ const target = e.target;
509
+ if (btnRef.current?.contains(target)) return;
510
+ if (dropRef.current?.contains(target)) return;
511
+ setOpen(false);
495
512
  };
496
513
  document.addEventListener("mousedown", handler);
497
514
  return () => document.removeEventListener("mousedown", handler);
@@ -504,13 +521,27 @@ function CustomSelect({
504
521
  document.addEventListener("keydown", handler);
505
522
  return () => document.removeEventListener("keydown", handler);
506
523
  }, [open]);
524
+ useEffect3(() => {
525
+ if (!open) return;
526
+ updatePos();
527
+ window.addEventListener("scroll", updatePos, true);
528
+ window.addEventListener("resize", updatePos);
529
+ return () => {
530
+ window.removeEventListener("scroll", updatePos, true);
531
+ window.removeEventListener("resize", updatePos);
532
+ };
533
+ }, [open, updatePos]);
507
534
  const textSize = size === "sm" ? "text-xs" : "text-sm";
508
- return /* @__PURE__ */ jsxs10("div", { ref, className: `relative ${className}`, children: [
535
+ return /* @__PURE__ */ jsxs10("div", { className, children: [
509
536
  /* @__PURE__ */ jsxs10(
510
537
  "button",
511
538
  {
539
+ ref: btnRef,
512
540
  type: "button",
513
- onClick: () => setOpen((o) => !o),
541
+ onClick: () => {
542
+ updatePos();
543
+ setOpen((o) => !o);
544
+ },
514
545
  className: `admin-input w-full flex items-center justify-between gap-2 ${textSize} text-left`,
515
546
  style: { color: selected ? "var(--text-primary)" : "var(--text-muted)" },
516
547
  children: [
@@ -537,40 +568,50 @@ function CustomSelect({
537
568
  ]
538
569
  }
539
570
  ),
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
571
+ open && createPortal3(
572
+ /* @__PURE__ */ jsx11(
573
+ "div",
574
+ {
575
+ ref: dropRef,
576
+ className: "py-1 max-h-48 overflow-y-auto",
577
+ style: {
578
+ position: "fixed",
579
+ top: pos.top,
580
+ left: pos.left,
581
+ width: pos.width,
582
+ minWidth: 120,
583
+ zIndex: 9999,
584
+ background: "var(--bg-elevated)",
585
+ border: "1px solid var(--border)",
586
+ borderRadius: "var(--radius-md)",
587
+ boxShadow: "0 8px 24px rgba(0,0,0,0.4)"
570
588
  },
571
- opt.value
572
- ))
573
- }
589
+ children: options.map((opt) => /* @__PURE__ */ jsx11(
590
+ "button",
591
+ {
592
+ type: "button",
593
+ onClick: () => {
594
+ onChange(opt.value);
595
+ setOpen(false);
596
+ },
597
+ className: `block w-full text-left px-3 py-1.5 ${textSize} transition-colors`,
598
+ style: {
599
+ color: opt.value === value ? "var(--accent-text)" : "var(--text-primary)",
600
+ background: opt.value === value ? "var(--accent-glow)" : "transparent"
601
+ },
602
+ onMouseEnter: (e) => {
603
+ if (opt.value !== value) e.currentTarget.style.background = "var(--bg-hover)";
604
+ },
605
+ onMouseLeave: (e) => {
606
+ e.currentTarget.style.background = opt.value === value ? "var(--accent-glow)" : "transparent";
607
+ },
608
+ children: opt.label
609
+ },
610
+ opt.value
611
+ ))
612
+ }
613
+ ),
614
+ document.body
574
615
  )
575
616
  ] });
576
617
  }
@@ -703,7 +744,7 @@ function AlertBanner({ type, title, children }) {
703
744
  }
704
745
 
705
746
  // src/components/AddressDisplay.tsx
706
- import { useState as useState4, useCallback } from "react";
747
+ import { useState as useState4, useCallback as useCallback2 } from "react";
707
748
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
708
749
  function truncateAddress(address) {
709
750
  const prefix = "DIRECT://";
@@ -720,7 +761,7 @@ function truncateAddress(address) {
720
761
  }
721
762
  function AddressDisplay({ address, nametag, truncate = true }) {
722
763
  const [copied, setCopied] = useState4(false);
723
- const handleCopy = useCallback(() => {
764
+ const handleCopy = useCallback2(() => {
724
765
  navigator.clipboard.writeText(address).then(() => {
725
766
  setCopied(true);
726
767
  setTimeout(() => setCopied(false), 1500);
@@ -774,7 +815,7 @@ function AddressDisplay({ address, nametag, truncate = true }) {
774
815
  }
775
816
 
776
817
  // src/components/JsonPanel.tsx
777
- import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback2 } from "react";
818
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback3 } from "react";
778
819
  import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
779
820
  function JsonPanel({
780
821
  value,
@@ -796,7 +837,7 @@ function JsonPanel({
796
837
  setText(JSON.stringify(clean, null, 2));
797
838
  setParseError(null);
798
839
  }, [value, isEditing, excludeKeys]);
799
- const handleChange = useCallback2((newText) => {
840
+ const handleChange = useCallback3((newText) => {
800
841
  setText(newText);
801
842
  try {
802
843
  const parsed = JSON.parse(newText);
@@ -806,10 +847,10 @@ function JsonPanel({
806
847
  setParseError(e instanceof Error ? e.message : "Invalid JSON");
807
848
  }
808
849
  }, [onChange]);
809
- const handleFocus = useCallback2(() => setIsEditing(true), []);
810
- const handleBlur = useCallback2(() => setIsEditing(false), []);
850
+ const handleFocus = useCallback3(() => setIsEditing(true), []);
851
+ const handleBlur = useCallback3(() => setIsEditing(false), []);
811
852
  const [copied, setCopied] = useState5(false);
812
- const handleCopy = useCallback2(async () => {
853
+ const handleCopy = useCallback3(async () => {
813
854
  await navigator.clipboard.writeText(text);
814
855
  setCopied(true);
815
856
  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.9",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",