@orangecheck/app-design 1.4.1 → 1.5.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.
package/dist/chrome.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { X, ChevronDown, Menu, ChevronRight, Sun, Moon, SunMoon, PanelLeft, CircleUser, LogOut, ArrowUpRight } from 'lucide-react';
1
+ import { X, ChevronDown, Menu, ChevronRight, Sun, Moon, SunMoon, PanelLeft, CircleUser, LogOut, ArrowUpRight, Ellipsis } from 'lucide-react';
2
2
  import Link from 'next/link';
3
3
  import { forwardRef, createContext, useContext, useState, useMemo, useEffect, useRef, useId, useSyncExternalStore, createElement } from 'react';
4
4
  import { clsx } from 'clsx';
@@ -349,7 +349,10 @@ var PopoverContent = forwardRef(function PopoverContent2({ className, align = "c
349
349
  }
350
350
  ) });
351
351
  });
352
- forwardRef(function TooltipContent2({ className, sideOffset = 6, ...props }, ref) {
352
+ var TooltipProvider = TooltipPrimitive.Provider;
353
+ var Tooltip = TooltipPrimitive.Root;
354
+ var TooltipTrigger = TooltipPrimitive.Trigger;
355
+ var TooltipContent = forwardRef(function TooltipContent2({ className, sideOffset = 6, ...props }, ref) {
353
356
  return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
354
357
  TooltipPrimitive.Content,
355
358
  {
@@ -600,48 +603,106 @@ function SectionSwitch({
600
603
  }
601
604
  );
602
605
  }
606
+ function NavBadge({ badge, className }) {
607
+ if (typeof badge === "number") {
608
+ return /* @__PURE__ */ jsx(
609
+ "span",
610
+ {
611
+ "aria-hidden": true,
612
+ className: cn(
613
+ "absolute flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[0.625rem] font-medium leading-none text-primary-foreground ring-2 ring-background oca-tabular",
614
+ className
615
+ ),
616
+ children: badge > 99 ? "99+" : badge
617
+ }
618
+ );
619
+ }
620
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("absolute size-2 rounded-full bg-oca-orange-deep ring-2 ring-background", className) });
621
+ }
622
+ var TAB = "flex min-w-0 flex-col items-center justify-center gap-1 rounded-2xl text-[0.6875rem] leading-none outline-none transition-colors [-webkit-tap-highlight-color:transparent] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring";
623
+ function TabFace({ icon, label, badge, active }) {
624
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
625
+ /* @__PURE__ */ jsxs(
626
+ "span",
627
+ {
628
+ className: cn(
629
+ "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
630
+ "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
631
+ active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
632
+ ),
633
+ children: [
634
+ icon,
635
+ badge ? /* @__PURE__ */ jsx(NavBadge, { badge, className: typeof badge === "number" ? "-top-1 right-0.5" : "right-2.5 top-0.5" }) : null
636
+ ]
637
+ }
638
+ ),
639
+ /* @__PURE__ */ jsx("span", { className: "max-w-full truncate px-0.5", children: label }),
640
+ badge ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: badge }) : null
641
+ ] });
642
+ }
603
643
  function TabBar({ items }) {
644
+ const [more, setMore] = useState(false);
645
+ const overflow = items.length > 5;
646
+ const shown = overflow ? items.slice(0, 4) : items;
647
+ const rest = overflow ? items.slice(4) : [];
648
+ const restBadge = rest.find((it) => it.badge)?.badge;
604
649
  return /* @__PURE__ */ jsx(
605
650
  "nav",
606
651
  {
607
652
  "aria-label": "Pages",
608
653
  "data-oca-tabbar": "",
609
654
  className: "fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/90 pb-[env(safe-area-inset-bottom)] backdrop-blur-md md:hidden",
610
- children: /* @__PURE__ */ jsx(
655
+ children: /* @__PURE__ */ jsxs(
611
656
  "div",
612
657
  {
613
658
  className: "mx-auto grid h-14 max-w-[32rem] px-1.5",
614
- style: { gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))` },
615
- children: items.map((it) => /* @__PURE__ */ jsxs(
616
- Link,
617
- {
618
- href: it.href,
619
- "aria-current": it.active ? "page" : void 0,
620
- className: cn(
621
- "flex min-w-0 flex-col items-center justify-center gap-1 rounded-2xl text-[0.6875rem] leading-none outline-none transition-colors [-webkit-tap-highlight-color:transparent] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
622
- it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"
659
+ style: { gridTemplateColumns: `repeat(${shown.length + (overflow ? 1 : 0)}, minmax(0, 1fr))` },
660
+ children: [
661
+ shown.map((it) => /* @__PURE__ */ jsx(
662
+ Link,
663
+ {
664
+ href: it.href,
665
+ "aria-current": it.active ? "page" : void 0,
666
+ className: cn(TAB, it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"),
667
+ children: /* @__PURE__ */ jsx(TabFace, { icon: it.icon, label: it.label, badge: it.badge, active: it.active })
668
+ },
669
+ it.href
670
+ )),
671
+ overflow ? /* @__PURE__ */ jsxs(Sheet, { open: more, onOpenChange: setMore, children: [
672
+ /* @__PURE__ */ jsx(
673
+ SheetTrigger,
674
+ {
675
+ className: cn(TAB, rest.some((it) => it.active) ? "text-foreground" : "text-muted-foreground hover:text-foreground"),
676
+ children: /* @__PURE__ */ jsx(TabFace, { icon: /* @__PURE__ */ jsx(Ellipsis, {}), label: "More", badge: restBadge ? true : void 0, active: rest.some((it) => it.active) })
677
+ }
623
678
  ),
624
- children: [
625
- /* @__PURE__ */ jsxs(
626
- "span",
679
+ /* @__PURE__ */ jsxs(SheetContent, { side: "bottom", className: "pb-[calc(1.5rem+env(safe-area-inset-bottom))]", children: [
680
+ /* @__PURE__ */ jsx(SheetTitle, { className: "px-1 text-[1.0625rem] font-medium", children: "More" }),
681
+ /* @__PURE__ */ jsx("div", { className: "mt-4 grid grid-cols-3 gap-2", children: rest.map((it) => /* @__PURE__ */ jsxs(
682
+ Link,
627
683
  {
684
+ href: it.href,
685
+ onClick: () => setMore(false),
686
+ "aria-current": it.active ? "page" : void 0,
628
687
  className: cn(
629
- "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
630
- "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
631
- it.active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
688
+ "flex min-h-20 flex-col items-center justify-center gap-2 rounded-2xl border px-2 text-center text-[0.8125rem] transition-colors",
689
+ "[&_svg]:size-5",
690
+ it.active ? "border-foreground bg-card text-foreground" : "border-border text-muted-foreground hover:text-foreground"
632
691
  ),
633
692
  children: [
634
- it.icon,
635
- it.badge ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute right-2.5 top-0.5 size-2 rounded-full bg-oca-orange ring-2 ring-background" }) : null
693
+ /* @__PURE__ */ jsxs("span", { className: "relative", children: [
694
+ it.icon,
695
+ it.badge ? /* @__PURE__ */ jsx(NavBadge, { badge: it.badge, className: typeof it.badge === "number" ? "-right-3 -top-2" : "-right-1 -top-0.5" }) : null
696
+ ] }),
697
+ /* @__PURE__ */ jsx("span", { className: "max-w-full truncate", children: it.label }),
698
+ it.badge ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: it.badge }) : null
636
699
  ]
637
- }
638
- ),
639
- /* @__PURE__ */ jsx("span", { className: "max-w-full truncate px-0.5", children: it.label }),
640
- it.badge ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: it.badge }) : null
641
- ]
642
- },
643
- it.href
644
- ))
700
+ },
701
+ it.href
702
+ )) })
703
+ ] })
704
+ ] }) : null
705
+ ]
645
706
  }
646
707
  )
647
708
  }
@@ -649,7 +710,11 @@ function TabBar({ items }) {
649
710
  }
650
711
  function tabsFor(groups) {
651
712
  const items = groups.flatMap((g) => g.items);
652
- return items.length >= 2 && items.length <= 5 && items.every((it) => it.icon) ? items : null;
713
+ return items.length >= 2 && items.every((it) => it.icon) ? items : null;
714
+ }
715
+ function sectionTabs(sections, current) {
716
+ if (!sections || !current || sections.length < 2 || !sections.every((sec) => sec.icon)) return null;
717
+ return sections.map((sec) => ({ href: sec.href, label: sec.label, icon: sec.icon, active: sec.id === current.id }));
653
718
  }
654
719
  var activeItem = (groups) => groups.flatMap((g) => g.items).find((it) => it.active);
655
720
  var warned = /* @__PURE__ */ new Set();
@@ -664,7 +729,10 @@ function warnDuplicateLabels(groups) {
664
729
  seen.add(it.label);
665
730
  }
666
731
  }
667
- function AppShell({
732
+ function AppShell(props) {
733
+ return props.variant === "rail" ? /* @__PURE__ */ jsx(RailShell, { ...props }) : /* @__PURE__ */ jsx(SidebarShell, { ...props });
734
+ }
735
+ function SidebarShell({
668
736
  nav,
669
737
  sections,
670
738
  activeSection,
@@ -682,7 +750,8 @@ function AppShell({
682
750
  const current = sections && sections.length > 0 ? sections.find((sec) => sec.id === activeSection) ?? sections.find((sec) => activeItem(sec.groups)) ?? sections[0] : void 0;
683
751
  const groups = current ? [...current.groups, ...persistent] : [...nav ?? [], ...persistent];
684
752
  warnDuplicateLabels(groups);
685
- const tabs = current ? null : tabsFor(groups);
753
+ const sectionTabsFor = sectionTabs(sections, current);
754
+ const tabs = current ? sectionTabsFor : tabsFor(groups);
686
755
  const page = activeItem(groups);
687
756
  const place = useMemo(
688
757
  () => ({
@@ -710,8 +779,8 @@ function AppShell({
710
779
  {
711
780
  className: cn(
712
781
  "inline-flex size-10 items-center justify-center rounded-full hover:bg-accent",
713
- // The tab bar holds every page; the sheet is left for a footer.
714
- tabs && !footer && "max-md:hidden"
782
+ // The tab bar holds every page; the sheet is left for a footer. Section tabs leave the pages here.
783
+ tabs && !sectionTabsFor && !footer && "max-md:hidden"
715
784
  ),
716
785
  "aria-label": "Open navigation",
717
786
  children: /* @__PURE__ */ jsx(Menu, { className: "size-5" })
@@ -719,8 +788,8 @@ function AppShell({
719
788
  ),
720
789
  /* @__PURE__ */ jsxs(SheetContent, { side: "left", className: "flex flex-col gap-6 overflow-y-auto p-4 pt-16", children: [
721
790
  /* @__PURE__ */ jsx(SheetTitle, { className: "sr-only", children: "Navigation" }),
722
- switcher,
723
- /* @__PURE__ */ jsx("div", { className: tabs ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
791
+ switcher ? /* @__PURE__ */ jsx("div", { className: sectionTabsFor ? "max-md:hidden" : void 0, children: switcher }) : null,
792
+ /* @__PURE__ */ jsx("div", { className: tabs && !sectionTabsFor ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
724
793
  footer ? /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer }) : null
725
794
  ] })
726
795
  ] }),
@@ -734,7 +803,7 @@ function AppShell({
734
803
  className: cn(
735
804
  "flex h-9 items-center gap-1.5 border-t border-border px-4 text-[0.875rem]",
736
805
  // The active tab already names the page.
737
- tabs && !pageOverride && "max-md:hidden"
806
+ tabs && !sectionTabsFor && !pageOverride && "max-md:hidden"
738
807
  ),
739
808
  children: [
740
809
  place.section ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -767,6 +836,64 @@ function AppShell({
767
836
  }
768
837
  ) });
769
838
  }
839
+ function RailItem({ item }) {
840
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
841
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
842
+ Link,
843
+ {
844
+ href: item.href,
845
+ "aria-current": item.active ? "page" : void 0,
846
+ className: cn(
847
+ "relative flex size-10 items-center justify-center rounded-xl outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring",
848
+ "[&_svg]:size-5 [&_svg]:shrink-0",
849
+ item.active ? "bg-card text-foreground shadow-sm ring-1 ring-border" : "text-muted-foreground hover:bg-accent hover:text-foreground"
850
+ ),
851
+ children: [
852
+ item.icon,
853
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
854
+ item.label,
855
+ item.badge ? /* @__PURE__ */ jsxs(Fragment, { children: [
856
+ ", ",
857
+ item.badge
858
+ ] }) : null
859
+ ] }),
860
+ item.badge ? /* @__PURE__ */ jsx(NavBadge, { badge: item.badge, className: typeof item.badge === "number" ? "-right-1 -top-1" : "right-1 top-1" }) : null
861
+ ]
862
+ }
863
+ ) }),
864
+ /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: item.label })
865
+ ] });
866
+ }
867
+ function RailShell({ nav = [], persistent = [], page: pageOverride, header, account, banner, children, className }) {
868
+ const main = nav.flatMap((g) => g.items);
869
+ const foot = persistent.flatMap((g) => g.items);
870
+ warnDuplicateLabels([...nav, ...persistent]);
871
+ const tabs = tabsFor([{ items: [...main, ...foot] }]);
872
+ const active = activeItem([...nav, ...persistent]);
873
+ const place = useMemo(() => ({ page: pageOverride ?? active?.label }), [pageOverride, active?.label]);
874
+ return /* @__PURE__ */ jsx(ShellContext.Provider, { value: place, children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs("div", { className: cn("flex h-dvh flex-col overflow-hidden bg-background", className), children: [
875
+ header,
876
+ banner,
877
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
878
+ /* @__PURE__ */ jsxs("nav", { "aria-label": "Pages", className: "hidden w-14 shrink-0 flex-col items-center gap-1 border-r border-border py-3 md:flex", children: [
879
+ main.map((it) => /* @__PURE__ */ jsx(RailItem, { item: it }, it.href)),
880
+ /* @__PURE__ */ jsxs("div", { className: "mt-auto flex flex-col items-center gap-1", children: [
881
+ foot.map((it) => /* @__PURE__ */ jsx(RailItem, { item: it }, it.href)),
882
+ account
883
+ ] })
884
+ ] }),
885
+ /* @__PURE__ */ jsx(
886
+ "main",
887
+ {
888
+ id: "main-content",
889
+ className: cn("flex min-h-0 min-w-0 flex-1 flex-col", tabs && "max-md:pb-[calc(3.5rem_+_env(safe-area-inset-bottom))]"),
890
+ children
891
+ }
892
+ )
893
+ ] }),
894
+ tabs ? /* @__PURE__ */ jsx(TabBar, { items: tabs }) : null
895
+ ] }) }) });
896
+ }
770
897
  function AccountMenu({
771
898
  name,
772
899
  detail,