@orangecheck/design 0.8.0 → 0.9.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.
@@ -6,7 +6,7 @@ var clsx = require('clsx');
6
6
  var tailwindMerge = require('tailwind-merge');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
  var Link6 = require('next/link');
9
- var react = require('react');
9
+ var React = require('react');
10
10
  var router = require('next/router');
11
11
  var authClient = require('@orangecheck/auth-client');
12
12
 
@@ -32,6 +32,7 @@ function _interopNamespace(e) {
32
32
 
33
33
  var Popover__namespace = /*#__PURE__*/_interopNamespace(Popover);
34
34
  var Link6__default = /*#__PURE__*/_interopDefault(Link6);
35
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
35
36
 
36
37
  // src/composites/help-hint.tsx
37
38
  function cn(...inputs) {
@@ -260,7 +261,7 @@ function DefinitionList({ items, className }) {
260
261
  "grid gap-x-5 gap-y-2 text-sm sm:grid-cols-[auto_1fr] sm:items-baseline",
261
262
  className
262
263
  ),
263
- children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
264
+ children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(React.Fragment, { children: [
264
265
  /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "label-mono text-muted-foreground text-[10px]", children: item.label }),
265
266
  /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "text-foreground font-mono text-xs break-all", children: item.value })
266
267
  ] }, i))
@@ -379,9 +380,9 @@ function EcosystemSwitcher({
379
380
  className,
380
381
  showOwnerEntries = false
381
382
  }) {
382
- const [open, setOpen] = react.useState(false);
383
- const containerRef = react.useRef(null);
384
- react.useEffect(() => {
383
+ const [open, setOpen] = React.useState(false);
384
+ const containerRef = React.useRef(null);
385
+ React.useEffect(() => {
385
386
  if (!open) return;
386
387
  function onDoc(e) {
387
388
  if (!containerRef.current) return;
@@ -464,6 +465,141 @@ function EcosystemSwitcher({
464
465
  )
465
466
  ] });
466
467
  }
468
+ var TOOLTIP_SURFACE_CLASS = "bg-popover text-popover-foreground border-border rounded-md border p-2.5 shadow-md";
469
+ var TOOLTIP_MOTION_CLASS = "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 origin-(--radix-popover-content-transform-origin) duration-150 ease-out";
470
+ function compose(theirs, ours) {
471
+ return (e) => {
472
+ theirs?.(e);
473
+ ours(e);
474
+ };
475
+ }
476
+ var lastInputWasKeyboard = false;
477
+ var modalityInstalled = false;
478
+ function installModalityTracking() {
479
+ if (modalityInstalled || typeof window === "undefined") return;
480
+ modalityInstalled = true;
481
+ window.addEventListener("keydown", () => lastInputWasKeyboard = true, true);
482
+ window.addEventListener("pointerdown", () => lastInputWasKeyboard = false, true);
483
+ }
484
+ function Tooltip({
485
+ content,
486
+ children,
487
+ side = "bottom",
488
+ align = "start",
489
+ sideOffset = 8,
490
+ collisionPadding = 12,
491
+ openDelay = 300,
492
+ closeDelay = 90,
493
+ disabled = false,
494
+ portal = true,
495
+ zIndexClassName = "z-50",
496
+ contentClassName
497
+ }) {
498
+ const [hovering, setHovering] = React__namespace.useState(false);
499
+ const [focusing, setFocusing] = React__namespace.useState(false);
500
+ const [isFinePointer, setIsFinePointer] = React__namespace.useState(false);
501
+ const openTimer = React__namespace.useRef(null);
502
+ const closeTimer = React__namespace.useRef(null);
503
+ React__namespace.useEffect(() => {
504
+ installModalityTracking();
505
+ const mq = window.matchMedia("(hover: hover) and (pointer: fine)");
506
+ const apply = () => setIsFinePointer(mq.matches);
507
+ apply();
508
+ mq.addEventListener("change", apply);
509
+ return () => mq.removeEventListener("change", apply);
510
+ }, []);
511
+ const clearTimers = React__namespace.useCallback(() => {
512
+ if (openTimer.current) clearTimeout(openTimer.current);
513
+ if (closeTimer.current) clearTimeout(closeTimer.current);
514
+ }, []);
515
+ React__namespace.useEffect(() => clearTimers, [clearTimers]);
516
+ React__namespace.useEffect(() => {
517
+ if (disabled) {
518
+ clearTimers();
519
+ setHovering(false);
520
+ }
521
+ }, [disabled, clearTimers]);
522
+ const handlePointerEnter = () => {
523
+ if (!isFinePointer) return;
524
+ if (closeTimer.current) clearTimeout(closeTimer.current);
525
+ openTimer.current = setTimeout(() => setHovering(true), openDelay);
526
+ };
527
+ const handlePointerLeave = () => {
528
+ if (openTimer.current) clearTimeout(openTimer.current);
529
+ closeTimer.current = setTimeout(() => setHovering(false), closeDelay);
530
+ };
531
+ const handlePointerDown = () => {
532
+ clearTimers();
533
+ setHovering(false);
534
+ setFocusing(false);
535
+ };
536
+ const handleFocus = () => {
537
+ if (lastInputWasKeyboard) {
538
+ clearTimers();
539
+ setFocusing(true);
540
+ }
541
+ };
542
+ const handleBlur = () => {
543
+ clearTimers();
544
+ setFocusing(false);
545
+ };
546
+ const handleKeyDown = (e) => {
547
+ if (e.key === "Escape") {
548
+ clearTimers();
549
+ setHovering(false);
550
+ setFocusing(false);
551
+ }
552
+ };
553
+ const effectiveOpen = !disabled && (isFinePointer && hovering || focusing);
554
+ const extra = {};
555
+ const childProps = children.props;
556
+ extra.onPointerEnter = compose(childProps.onPointerEnter, handlePointerEnter);
557
+ extra.onPointerLeave = compose(childProps.onPointerLeave, handlePointerLeave);
558
+ extra.onPointerDown = compose(childProps.onPointerDown, handlePointerDown);
559
+ extra.onFocus = compose(childProps.onFocus, handleFocus);
560
+ extra.onBlur = compose(childProps.onBlur, handleBlur);
561
+ extra.onKeyDown = compose(childProps.onKeyDown, handleKeyDown);
562
+ const trigger = React__namespace.cloneElement(children, extra);
563
+ const contentEl = /* @__PURE__ */ jsxRuntime.jsx(
564
+ Popover__namespace.Content,
565
+ {
566
+ side,
567
+ align,
568
+ sideOffset,
569
+ collisionPadding,
570
+ "aria-hidden": true,
571
+ onOpenAutoFocus: (e) => e.preventDefault(),
572
+ onCloseAutoFocus: (e) => e.preventDefault(),
573
+ onFocusOutside: (e) => e.preventDefault(),
574
+ className: cn(
575
+ TOOLTIP_SURFACE_CLASS,
576
+ TOOLTIP_MOTION_CLASS,
577
+ "pointer-events-none",
578
+ zIndexClassName,
579
+ contentClassName
580
+ ),
581
+ children: content
582
+ }
583
+ );
584
+ return /* @__PURE__ */ jsxRuntime.jsxs(
585
+ Popover__namespace.Root,
586
+ {
587
+ open: effectiveOpen,
588
+ onOpenChange: (o) => {
589
+ if (!o) {
590
+ clearTimers();
591
+ setHovering(false);
592
+ setFocusing(false);
593
+ }
594
+ },
595
+ modal: false,
596
+ children: [
597
+ /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Anchor, { asChild: true, children: trigger }),
598
+ portal ? /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Portal, { children: contentEl }) : contentEl
599
+ ]
600
+ }
601
+ );
602
+ }
467
603
 
468
604
  // src/chrome/family-properties.ts
469
605
  var FAMILY_PROPERTIES = [
@@ -628,6 +764,31 @@ function MenuCategoryChip({ category }) {
628
764
  }
629
765
  );
630
766
  }
767
+ function SwitcherTooltipCard({ current }) {
768
+ const prop = findFamilyProperty(current);
769
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-[13rem] max-w-[calc(100vw-1.5rem)]", children: [
770
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col leading-tight", children: [
771
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-wrap items-baseline gap-x-1.5", children: [
772
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-mono text-[9px] tracking-widest uppercase", children: "you\u2019re on" }),
773
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary font-display text-[12px] font-semibold tracking-tight", children: prop?.label ?? "orangecheck family" })
774
+ ] }),
775
+ prop?.sub && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground mt-0.5 font-mono text-[10px] tracking-wide", children: prop.sub })
776
+ ] }),
777
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-border -mx-2.5 my-2 border-t" }),
778
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
779
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { "aria-hidden": true, className: "text-muted-foreground h-3 w-3 shrink-0" }),
780
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary font-mono text-[10px] font-medium tracking-widest uppercase", children: "click" }),
781
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-mono text-[10px]", children: "\xB7" }),
782
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-mono text-[10px] tracking-wide", children: "switch site" })
783
+ ] }),
784
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center gap-1.5", children: [
785
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CornerDownLeft, { "aria-hidden": true, className: "text-muted-foreground h-3 w-3 shrink-0" }),
786
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary font-mono text-[10px] font-medium tracking-widest uppercase", children: "double-click" }),
787
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-mono text-[10px]", children: "\xB7" }),
788
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-mono text-[10px] tracking-wide", children: "home" })
789
+ ] })
790
+ ] });
791
+ }
631
792
  function OcLogoDropdown({
632
793
  current,
633
794
  homeHref = "/",
@@ -638,9 +799,9 @@ function OcLogoDropdown({
638
799
  popoverClassName,
639
800
  showOwnerEntries = false
640
801
  }) {
641
- const [open, setOpen] = react.useState(false);
642
- const containerRef = react.useRef(null);
643
- const menuId = react.useId();
802
+ const [open, setOpen] = React.useState(false);
803
+ const containerRef = React.useRef(null);
804
+ const menuId = React.useId();
644
805
  const router$1 = router.useRouter();
645
806
  const currentCategory = findFamilyProperty(current)?.category ?? "hub";
646
807
  function handleTriggerClick(e) {
@@ -652,7 +813,7 @@ function OcLogoDropdown({
652
813
  }
653
814
  setOpen((v) => !v);
654
815
  }
655
- react.useEffect(() => {
816
+ React.useEffect(() => {
656
817
  if (!open) return;
657
818
  function onDoc(e) {
658
819
  if (!containerRef.current) return;
@@ -677,31 +838,45 @@ function OcLogoDropdown({
677
838
  "data-oc-current-category": currentCategory,
678
839
  "data-oc-site-state": siteState,
679
840
  children: [
680
- /* @__PURE__ */ jsxRuntime.jsxs(
681
- "button",
841
+ /* @__PURE__ */ jsxRuntime.jsx(
842
+ Tooltip,
682
843
  {
683
- type: "button",
684
- "aria-haspopup": "menu",
685
- "aria-expanded": open,
686
- "aria-controls": menuId,
687
- "aria-label": "OrangeCheck family \xB7 click to open property menu, double-click to go home",
688
- title: "single-click \xB7 family menu \xB7 double-click \xB7 go home",
689
- onClick: handleTriggerClick,
690
- className: triggerClassName ?? "group hover:bg-accent/30 -mx-1.5 -my-1 flex min-h-[40px] items-center gap-2 rounded-sm px-1.5 py-1 transition-colors " + (open ? "bg-accent/20" : ""),
691
- "data-oc-logo-dropdown-trigger": "",
692
- "data-oc-logo-dropdown-open": open ? "true" : "false",
693
- children: [
694
- children,
695
- /* @__PURE__ */ jsxRuntime.jsx(CategoryChip, { category: currentCategory }),
696
- /* @__PURE__ */ jsxRuntime.jsx(SiteStateBadge, { state: siteState }),
697
- /* @__PURE__ */ jsxRuntime.jsx(
698
- lucideReact.ChevronDown,
699
- {
700
- "aria-hidden": true,
701
- className: "text-muted-foreground/70 group-hover:text-foreground/80 h-3.5 w-3.5 shrink-0 transition-transform duration-200 " + (open ? "rotate-180" : "")
702
- }
703
- )
704
- ]
844
+ content: /* @__PURE__ */ jsxRuntime.jsx(SwitcherTooltipCard, { current }),
845
+ disabled: open,
846
+ side: "bottom",
847
+ align: "start",
848
+ sideOffset: 8,
849
+ zIndexClassName: "z-[70]",
850
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
851
+ "button",
852
+ {
853
+ type: "button",
854
+ "aria-haspopup": "menu",
855
+ "aria-expanded": open,
856
+ "aria-controls": menuId,
857
+ "aria-label": "orangecheck family menu",
858
+ onClick: handleTriggerClick,
859
+ className: cn(
860
+ // focus indicator is guaranteed even if a consumer overrides triggerClassName
861
+ "focus-visible:ring-primary/60 rounded-sm focus-visible:ring-2 focus-visible:outline-none",
862
+ triggerClassName ?? "group hover:bg-accent/30 -mx-1.5 -my-1 flex min-h-[40px] items-center gap-2 rounded-sm px-1.5 py-1 transition-colors " + (open ? "bg-accent/20" : "")
863
+ ),
864
+ "data-oc-logo-dropdown-trigger": "",
865
+ "data-oc-logo-dropdown-open": open ? "true" : "false",
866
+ children: [
867
+ children,
868
+ /* @__PURE__ */ jsxRuntime.jsx(CategoryChip, { category: currentCategory }),
869
+ /* @__PURE__ */ jsxRuntime.jsx(SiteStateBadge, { state: siteState }),
870
+ /* @__PURE__ */ jsxRuntime.jsx(
871
+ lucideReact.ChevronDown,
872
+ {
873
+ "aria-hidden": true,
874
+ className: "text-muted-foreground/70 group-hover:text-foreground/80 h-3.5 w-3.5 shrink-0 transition-transform duration-200 " + (open ? "rotate-180" : "")
875
+ }
876
+ )
877
+ ]
878
+ }
879
+ )
705
880
  }
706
881
  ),
707
882
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -730,7 +905,7 @@ function OcLogoDropdown({
730
905
  {
731
906
  id: menuId,
732
907
  role: "menu",
733
- "aria-label": "OrangeCheck family",
908
+ "aria-label": "orangecheck family",
734
909
  className: popoverClassName ?? "bg-background absolute top-full left-0 z-[60] mt-2 w-[min(20rem,calc(100vw-1rem))] border shadow-lg",
735
910
  "data-oc-logo-dropdown-popover": "",
736
911
  children: [
@@ -839,9 +1014,9 @@ async function writeClipboard(text) {
839
1014
  }
840
1015
  }
841
1016
  function CopyableDid({ did }) {
842
- const [copied, setCopied] = react.useState(false);
843
- const timerRef = react.useRef(null);
844
- react.useEffect(
1017
+ const [copied, setCopied] = React.useState(false);
1018
+ const timerRef = React.useRef(null);
1019
+ React.useEffect(
845
1020
  () => () => {
846
1021
  if (timerRef.current) clearTimeout(timerRef.current);
847
1022
  },
@@ -887,9 +1062,9 @@ function PromoteRow({
887
1062
  disabled,
888
1063
  onPromote
889
1064
  }) {
890
- const [copied, setCopied] = react.useState(false);
891
- const timerRef = react.useRef(null);
892
- react.useEffect(
1065
+ const [copied, setCopied] = React.useState(false);
1066
+ const timerRef = React.useRef(null);
1067
+ React.useEffect(
893
1068
  () => () => {
894
1069
  if (timerRef.current) clearTimeout(timerRef.current);
895
1070
  },
@@ -960,12 +1135,12 @@ function IdentityPromoteSection({
960
1135
  open,
961
1136
  setDisplayIdentity
962
1137
  }) {
963
- const [identities, setIdentities] = react.useState(null);
964
- const [loadFailed, setLoadFailed] = react.useState(false);
965
- const [promoting, setPromoting] = react.useState(null);
966
- const [promoteErr, setPromoteErr] = react.useState(null);
967
- const lastFetchKeyRef = react.useRef(null);
968
- react.useEffect(() => {
1138
+ const [identities, setIdentities] = React.useState(null);
1139
+ const [loadFailed, setLoadFailed] = React.useState(false);
1140
+ const [promoting, setPromoting] = React.useState(null);
1141
+ const [promoteErr, setPromoteErr] = React.useState(null);
1142
+ const lastFetchKeyRef = React.useRef(null);
1143
+ React.useEffect(() => {
969
1144
  if (!open) return;
970
1145
  if (lastFetchKeyRef.current === didOc) return;
971
1146
  lastFetchKeyRef.current = didOc;
@@ -1074,15 +1249,15 @@ function OcAccountMenuView({
1074
1249
  switchAccount,
1075
1250
  addAccountUrl
1076
1251
  } = session;
1077
- const [hydrated, setHydrated] = react.useState(false);
1078
- const [open, setOpen] = react.useState(false);
1079
- const wrapRef = react.useRef(null);
1252
+ const [hydrated, setHydrated] = React.useState(false);
1253
+ const [open, setOpen] = React.useState(false);
1254
+ const wrapRef = React.useRef(null);
1080
1255
  const property = findFamilyProperty(current);
1081
1256
  const hostname = property?.hostname ?? `${current}.ochk.io`;
1082
1257
  const isHome = current === "home";
1083
1258
  const familyDashboardEnabled = showFamilyDashboard ?? !isHome;
1084
- react.useEffect(() => setHydrated(true), []);
1085
- react.useEffect(() => {
1259
+ React.useEffect(() => setHydrated(true), []);
1260
+ React.useEffect(() => {
1086
1261
  const onWake = () => {
1087
1262
  if (document.visibilityState === "visible") void refresh();
1088
1263
  };
@@ -1094,7 +1269,7 @@ function OcAccountMenuView({
1094
1269
  window.removeEventListener("focus", onFocus);
1095
1270
  };
1096
1271
  }, [refresh]);
1097
- react.useEffect(() => {
1272
+ React.useEffect(() => {
1098
1273
  if (!open) return;
1099
1274
  const onClick = (e) => {
1100
1275
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -1330,8 +1505,8 @@ function AccountsSection({
1330
1505
  signOut,
1331
1506
  onActionStart
1332
1507
  }) {
1333
- const [switching, setSwitching] = react.useState(null);
1334
- const [err, setErr] = react.useState(null);
1508
+ const [switching, setSwitching] = React.useState(null);
1509
+ const [err, setErr] = React.useState(null);
1335
1510
  if (!addAccountUrl) return null;
1336
1511
  const onSwitch = async (didOc) => {
1337
1512
  if (!switchAccount || switching) return;
@@ -1594,8 +1769,8 @@ function OcDashboardShell({
1594
1769
  children,
1595
1770
  className
1596
1771
  }) {
1597
- const [drawerOpen, setDrawerOpen] = react.useState(false);
1598
- react.useEffect(() => {
1772
+ const [drawerOpen, setDrawerOpen] = React.useState(false);
1773
+ React.useEffect(() => {
1599
1774
  if (!drawerOpen) return;
1600
1775
  function onKey(e) {
1601
1776
  if (e.key === "Escape") setDrawerOpen(false);