@orangecheck/design 0.7.6 → 0.9.0

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.
@@ -4,6 +4,7 @@ import { clsx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
6
6
  import Link6 from 'next/link';
7
+ import * as React from 'react';
7
8
  import { Fragment, useState, useRef, useEffect, useId } from 'react';
8
9
  import { useRouter } from 'next/router';
9
10
  import { useOcSession, fetchOcLinkedIdentities } from '@orangecheck/auth-client';
@@ -439,6 +440,141 @@ function EcosystemSwitcher({
439
440
  )
440
441
  ] });
441
442
  }
443
+ var TOOLTIP_SURFACE_CLASS = "bg-popover text-popover-foreground border-border rounded-md border p-2.5 shadow-md";
444
+ 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";
445
+ function compose(theirs, ours) {
446
+ return (e) => {
447
+ theirs?.(e);
448
+ ours(e);
449
+ };
450
+ }
451
+ var lastInputWasKeyboard = false;
452
+ var modalityInstalled = false;
453
+ function installModalityTracking() {
454
+ if (modalityInstalled || typeof window === "undefined") return;
455
+ modalityInstalled = true;
456
+ window.addEventListener("keydown", () => lastInputWasKeyboard = true, true);
457
+ window.addEventListener("pointerdown", () => lastInputWasKeyboard = false, true);
458
+ }
459
+ function Tooltip({
460
+ content,
461
+ children,
462
+ side = "bottom",
463
+ align = "start",
464
+ sideOffset = 8,
465
+ collisionPadding = 12,
466
+ openDelay = 300,
467
+ closeDelay = 90,
468
+ disabled = false,
469
+ portal = true,
470
+ zIndexClassName = "z-50",
471
+ contentClassName
472
+ }) {
473
+ const [hovering, setHovering] = React.useState(false);
474
+ const [focusing, setFocusing] = React.useState(false);
475
+ const [isFinePointer, setIsFinePointer] = React.useState(false);
476
+ const openTimer = React.useRef(null);
477
+ const closeTimer = React.useRef(null);
478
+ React.useEffect(() => {
479
+ installModalityTracking();
480
+ const mq = window.matchMedia("(hover: hover) and (pointer: fine)");
481
+ const apply = () => setIsFinePointer(mq.matches);
482
+ apply();
483
+ mq.addEventListener("change", apply);
484
+ return () => mq.removeEventListener("change", apply);
485
+ }, []);
486
+ const clearTimers = React.useCallback(() => {
487
+ if (openTimer.current) clearTimeout(openTimer.current);
488
+ if (closeTimer.current) clearTimeout(closeTimer.current);
489
+ }, []);
490
+ React.useEffect(() => clearTimers, [clearTimers]);
491
+ React.useEffect(() => {
492
+ if (disabled) {
493
+ clearTimers();
494
+ setHovering(false);
495
+ }
496
+ }, [disabled, clearTimers]);
497
+ const handlePointerEnter = () => {
498
+ if (!isFinePointer) return;
499
+ if (closeTimer.current) clearTimeout(closeTimer.current);
500
+ openTimer.current = setTimeout(() => setHovering(true), openDelay);
501
+ };
502
+ const handlePointerLeave = () => {
503
+ if (openTimer.current) clearTimeout(openTimer.current);
504
+ closeTimer.current = setTimeout(() => setHovering(false), closeDelay);
505
+ };
506
+ const handlePointerDown = () => {
507
+ clearTimers();
508
+ setHovering(false);
509
+ setFocusing(false);
510
+ };
511
+ const handleFocus = () => {
512
+ if (lastInputWasKeyboard) {
513
+ clearTimers();
514
+ setFocusing(true);
515
+ }
516
+ };
517
+ const handleBlur = () => {
518
+ clearTimers();
519
+ setFocusing(false);
520
+ };
521
+ const handleKeyDown = (e) => {
522
+ if (e.key === "Escape") {
523
+ clearTimers();
524
+ setHovering(false);
525
+ setFocusing(false);
526
+ }
527
+ };
528
+ const effectiveOpen = !disabled && (isFinePointer && hovering || focusing);
529
+ const extra = {};
530
+ const childProps = children.props;
531
+ extra.onPointerEnter = compose(childProps.onPointerEnter, handlePointerEnter);
532
+ extra.onPointerLeave = compose(childProps.onPointerLeave, handlePointerLeave);
533
+ extra.onPointerDown = compose(childProps.onPointerDown, handlePointerDown);
534
+ extra.onFocus = compose(childProps.onFocus, handleFocus);
535
+ extra.onBlur = compose(childProps.onBlur, handleBlur);
536
+ extra.onKeyDown = compose(childProps.onKeyDown, handleKeyDown);
537
+ const trigger = React.cloneElement(children, extra);
538
+ const contentEl = /* @__PURE__ */ jsx(
539
+ Popover.Content,
540
+ {
541
+ side,
542
+ align,
543
+ sideOffset,
544
+ collisionPadding,
545
+ "aria-hidden": true,
546
+ onOpenAutoFocus: (e) => e.preventDefault(),
547
+ onCloseAutoFocus: (e) => e.preventDefault(),
548
+ onFocusOutside: (e) => e.preventDefault(),
549
+ className: cn(
550
+ TOOLTIP_SURFACE_CLASS,
551
+ TOOLTIP_MOTION_CLASS,
552
+ "pointer-events-none",
553
+ zIndexClassName,
554
+ contentClassName
555
+ ),
556
+ children: content
557
+ }
558
+ );
559
+ return /* @__PURE__ */ jsxs(
560
+ Popover.Root,
561
+ {
562
+ open: effectiveOpen,
563
+ onOpenChange: (o) => {
564
+ if (!o) {
565
+ clearTimers();
566
+ setHovering(false);
567
+ setFocusing(false);
568
+ }
569
+ },
570
+ modal: false,
571
+ children: [
572
+ /* @__PURE__ */ jsx(Popover.Anchor, { asChild: true, children: trigger }),
573
+ portal ? /* @__PURE__ */ jsx(Popover.Portal, { children: contentEl }) : contentEl
574
+ ]
575
+ }
576
+ );
577
+ }
442
578
 
443
579
  // src/chrome/family-properties.ts
444
580
  var FAMILY_PROPERTIES = [
@@ -603,6 +739,31 @@ function MenuCategoryChip({ category }) {
603
739
  }
604
740
  );
605
741
  }
742
+ function SwitcherTooltipCard({ current }) {
743
+ const prop = findFamilyProperty(current);
744
+ return /* @__PURE__ */ jsxs("div", { className: "w-[13rem] max-w-[calc(100vw-1.5rem)]", children: [
745
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col leading-tight", children: [
746
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-baseline gap-x-1.5", children: [
747
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[9px] tracking-widest uppercase", children: "you\u2019re on" }),
748
+ /* @__PURE__ */ jsx("span", { className: "text-primary font-display text-[12px] font-semibold tracking-tight", children: prop?.label ?? "orangecheck family" })
749
+ ] }),
750
+ prop?.sub && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground mt-0.5 font-mono text-[10px] tracking-wide", children: prop.sub })
751
+ ] }),
752
+ /* @__PURE__ */ jsx("div", { className: "border-border -mx-2.5 my-2 border-t" }),
753
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
754
+ /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "text-muted-foreground h-3 w-3 shrink-0" }),
755
+ /* @__PURE__ */ jsx("span", { className: "text-primary font-mono text-[10px] font-medium tracking-widest uppercase", children: "click" }),
756
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[10px]", children: "\xB7" }),
757
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[10px] tracking-wide", children: "switch site" })
758
+ ] }),
759
+ /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex items-center gap-1.5", children: [
760
+ /* @__PURE__ */ jsx(CornerDownLeft, { "aria-hidden": true, className: "text-muted-foreground h-3 w-3 shrink-0" }),
761
+ /* @__PURE__ */ jsx("span", { className: "text-primary font-mono text-[10px] font-medium tracking-widest uppercase", children: "double-click" }),
762
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[10px]", children: "\xB7" }),
763
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[10px] tracking-wide", children: "home" })
764
+ ] })
765
+ ] });
766
+ }
606
767
  function OcLogoDropdown({
607
768
  current,
608
769
  homeHref = "/",
@@ -652,31 +813,45 @@ function OcLogoDropdown({
652
813
  "data-oc-current-category": currentCategory,
653
814
  "data-oc-site-state": siteState,
654
815
  children: [
655
- /* @__PURE__ */ jsxs(
656
- "button",
816
+ /* @__PURE__ */ jsx(
817
+ Tooltip,
657
818
  {
658
- type: "button",
659
- "aria-haspopup": "menu",
660
- "aria-expanded": open,
661
- "aria-controls": menuId,
662
- "aria-label": "OrangeCheck family \xB7 click to open property menu, double-click to go home",
663
- title: "single-click \xB7 family menu \xB7 double-click \xB7 go home",
664
- onClick: handleTriggerClick,
665
- 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" : ""),
666
- "data-oc-logo-dropdown-trigger": "",
667
- "data-oc-logo-dropdown-open": open ? "true" : "false",
668
- children: [
669
- children,
670
- /* @__PURE__ */ jsx(CategoryChip, { category: currentCategory }),
671
- /* @__PURE__ */ jsx(SiteStateBadge, { state: siteState }),
672
- /* @__PURE__ */ jsx(
673
- ChevronDown,
674
- {
675
- "aria-hidden": true,
676
- 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" : "")
677
- }
678
- )
679
- ]
819
+ content: /* @__PURE__ */ jsx(SwitcherTooltipCard, { current }),
820
+ disabled: open,
821
+ side: "bottom",
822
+ align: "start",
823
+ sideOffset: 8,
824
+ zIndexClassName: "z-[70]",
825
+ children: /* @__PURE__ */ jsxs(
826
+ "button",
827
+ {
828
+ type: "button",
829
+ "aria-haspopup": "menu",
830
+ "aria-expanded": open,
831
+ "aria-controls": menuId,
832
+ "aria-label": "orangecheck family menu",
833
+ onClick: handleTriggerClick,
834
+ className: cn(
835
+ // focus indicator is guaranteed even if a consumer overrides triggerClassName
836
+ "focus-visible:ring-primary/60 rounded-sm focus-visible:ring-2 focus-visible:outline-none",
837
+ 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" : "")
838
+ ),
839
+ "data-oc-logo-dropdown-trigger": "",
840
+ "data-oc-logo-dropdown-open": open ? "true" : "false",
841
+ children: [
842
+ children,
843
+ /* @__PURE__ */ jsx(CategoryChip, { category: currentCategory }),
844
+ /* @__PURE__ */ jsx(SiteStateBadge, { state: siteState }),
845
+ /* @__PURE__ */ jsx(
846
+ ChevronDown,
847
+ {
848
+ "aria-hidden": true,
849
+ 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" : "")
850
+ }
851
+ )
852
+ ]
853
+ }
854
+ )
680
855
  }
681
856
  ),
682
857
  /* @__PURE__ */ jsxs(
@@ -705,7 +880,7 @@ function OcLogoDropdown({
705
880
  {
706
881
  id: menuId,
707
882
  role: "menu",
708
- "aria-label": "OrangeCheck family",
883
+ "aria-label": "orangecheck family",
709
884
  className: popoverClassName ?? "bg-background absolute top-full left-0 z-[60] mt-2 w-[min(20rem,calc(100vw-1rem))] border shadow-lg",
710
885
  "data-oc-logo-dropdown-popover": "",
711
886
  children: [