@the-portland-company/shell 0.8.0 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0 — 2026-07-12
4
+
5
+ ### Fixed (Chakra chrome — reconciled with main app)
6
+
7
+ - **`NavItem` re-synced to main-app `AuthenticatedLayout`**: removed the orange→red gradient hover sweep that had diverged from production chrome; restored gray hover, parent active when any child route matches (including Tabs Only), and the effectiveHref vs href-less accordion header split. `brandIcon` + `safeHref` shell additions retained.
8
+
9
+ ### Note for hosts
10
+
11
+ - Main VRM app now renders chrome exclusively via `ShellChrome` + `AppLayout` (no dual `AuthenticatedLayoutFrame` chrome path). Contacts already used this path — both apps should pin `^0.9.0`.
12
+
3
13
  ## 0.8.0 — 2026-07-02
4
14
 
5
15
  ### Added (backward-compatible)
package/dist/index.cjs CHANGED
@@ -1573,8 +1573,7 @@ function BrandIcon({
1573
1573
  );
1574
1574
  }
1575
1575
  var BrandIcon_default = BrandIcon;
1576
- var MODE_CAMPAIGN = "campaign";
1577
- var MODE_PETITION = "petition";
1576
+ var safeHref = (h) => h && /^(https?:|mailto:|tel:|\/|#|\.)/i.test(h.trim()) ? h : "#";
1578
1577
  function NavItem({
1579
1578
  icon,
1580
1579
  brandIcon,
@@ -1595,23 +1594,9 @@ function NavItem({
1595
1594
  const location = reactRouterDom.useLocation();
1596
1595
  const navigate = reactRouterDom.useNavigate();
1597
1596
  const mode = useShellMode();
1598
- const currentMode = mode?.currentMode ?? null;
1599
1597
  const modeConfig = resolveModeConfig(mode);
1600
1598
  const navPref = useShellNavigationPreference();
1601
1599
  const showSubMenu = navPref?.showSubMenu ?? true;
1602
- const navHoverGradient = currentMode === MODE_CAMPAIGN ? "linear-gradient(90deg, #F2C94C 0%, #E63946 100%)" : currentMode === MODE_PETITION ? "linear-gradient(90deg, #F2C94C 0%, #007BFF 100%)" : "linear-gradient(90deg, #ea6a3a 0%, #E63946 100%)";
1603
- const gradientSweepBefore = {
1604
- content: '""',
1605
- position: "absolute",
1606
- top: 0,
1607
- left: 0,
1608
- bottom: 0,
1609
- width: "0%",
1610
- background: navHoverGradient,
1611
- transition: "width 300ms ease",
1612
- pointerEvents: "none",
1613
- zIndex: 0
1614
- };
1615
1600
  const isDisabled = Boolean(navIsDisabled || disabled);
1616
1601
  const effectiveHref = href || (Array.isArray(subItems) && subItems.length > 0 ? subItems[0].href : void 0);
1617
1602
  const effectivePathname = effectiveHref?.split("?")[0];
@@ -1621,6 +1606,7 @@ function NavItem({
1621
1606
  [location.search]
1622
1607
  );
1623
1608
  const matchesSubItem = (itemHref) => {
1609
+ if (!itemHref) return false;
1624
1610
  if (itemHref === currentFullPath) return true;
1625
1611
  const itemPath = itemHref.split("?")[0];
1626
1612
  const itemSearch = itemHref.includes("?") ? itemHref.split("?")[1] : "";
@@ -1664,83 +1650,130 @@ function NavItem({
1664
1650
  return effectivePathname !== `/${location.pathname.split("/")[1]}/dashboard` && location.pathname.startsWith(effectivePathname + "/");
1665
1651
  })();
1666
1652
  const hasSubItems = showSubMenu && Array.isArray(subItems) && subItems.length > 0;
1667
- const hasActiveChild = hasSubItems && subItems.some((item) => matchesSubItem(item.href || ""));
1653
+ const hasActiveChild = Array.isArray(subItems) && subItems.some((item) => matchesSubItem(item.href || ""));
1668
1654
  const isParentActive = isActive || hasActiveChild;
1669
1655
  const isSubMenuOpen = isExpanded !== void 0 ? isExpanded : hasSubItems && subItems.some((item) => matchesSubItem(item.href || ""));
1670
1656
  const expandedRowBg = isParentActive ? modeConfig?.primaryColor || "brand.500" : isSubMenuOpen ? "gray.100" : "transparent";
1671
1657
  const expandedRowTextColor = isParentActive ? "white" : isSubMenuOpen ? "gray.900" : "gray.600";
1672
1658
  const disabledRowBg = isSubItem ? "gray.50" : "transparent";
1673
1659
  const disabledRowTextColor = "gray.400";
1660
+ const renderIcon = (activeForHover) => {
1661
+ if (brandIcon) {
1662
+ return /* @__PURE__ */ jsxRuntime.jsx(
1663
+ react.Box,
1664
+ {
1665
+ mr: "4",
1666
+ display: "inline-flex",
1667
+ alignItems: "center",
1668
+ filter: isParentActive || isActive ? "brightness(0) invert(1)" : void 0,
1669
+ _groupHover: isDisabled ? void 0 : {
1670
+ filter: activeForHover ? "brightness(0) invert(1)" : void 0
1671
+ },
1672
+ children: /* @__PURE__ */ jsxRuntime.jsx(BrandIcon_default, { name: brandIcon, size: 18 })
1673
+ }
1674
+ );
1675
+ }
1676
+ if (icon) {
1677
+ return /* @__PURE__ */ jsxRuntime.jsx(
1678
+ react.Box,
1679
+ {
1680
+ mr: "4",
1681
+ fontSize: "16",
1682
+ _groupHover: {
1683
+ color: isDisabled ? disabledRowTextColor : activeForHover ? "white" : "gray.900"
1684
+ },
1685
+ as: icon
1686
+ }
1687
+ );
1688
+ }
1689
+ return null;
1690
+ };
1674
1691
  if (hasSubItems) {
1675
- const headerProps = {
1676
- align: "center",
1677
- px: "4",
1678
- py: "2.5",
1679
- mx: "4",
1680
- w: "auto",
1681
- borderTopRadius: "lg",
1682
- borderBottomRadius: isSubMenuOpen ? 0 : "lg",
1683
- role: "group",
1684
- cursor: isDisabled ? "not-allowed" : "pointer",
1685
- opacity: 1,
1686
- pointerEvents: isDisabled ? "none" : "auto",
1687
- "data-nav-item-active": String(isParentActive),
1688
- position: "relative",
1689
- overflow: "hidden",
1690
- bg: isDisabled ? disabledRowBg : expandedRowBg,
1691
- color: isDisabled ? disabledRowTextColor : expandedRowTextColor,
1692
- _before: isDisabled ? void 0 : gradientSweepBefore,
1693
- _hover: {
1694
- color: isDisabled ? disabledRowTextColor : "white",
1695
- _before: isDisabled ? void 0 : { width: "100%" }
1696
- },
1697
- "aria-disabled": isDisabled ? "true" : "false",
1698
- onClick: (e) => {
1699
- if (isDisabled) return;
1700
- if (effectiveHref) e.preventDefault();
1701
- e.stopPropagation();
1702
- onToggle?.(menuKey || null);
1703
- }
1704
- };
1705
1692
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { children: [
1706
- /* @__PURE__ */ jsxRuntime.jsxs(react.Flex, { ...headerProps, ...rest, children: [
1707
- brandIcon ? /* @__PURE__ */ jsxRuntime.jsx(
1708
- react.Box,
1709
- {
1710
- mr: "4",
1711
- display: "inline-flex",
1712
- alignItems: "center",
1713
- position: "relative",
1714
- zIndex: 1,
1715
- filter: isParentActive ? "brightness(0) invert(1)" : void 0,
1716
- _groupHover: isDisabled ? void 0 : { filter: "brightness(0) invert(1)" },
1717
- children: /* @__PURE__ */ jsxRuntime.jsx(BrandIcon_default, { name: brandIcon, size: 18 })
1718
- }
1719
- ) : icon ? /* @__PURE__ */ jsxRuntime.jsx(
1720
- react.Box,
1721
- {
1722
- mr: "4",
1723
- fontSize: "16",
1724
- position: "relative",
1725
- zIndex: 1,
1726
- _groupHover: {
1727
- color: isDisabled ? disabledRowTextColor : "white"
1728
- },
1729
- as: icon
1730
- }
1731
- ) : null,
1732
- /* @__PURE__ */ jsxRuntime.jsx(react.Box, { flex: "1", position: "relative", zIndex: 1, children }),
1733
- /* @__PURE__ */ jsxRuntime.jsx(
1734
- react.Box,
1735
- {
1736
- as: fi.FiChevronDown,
1737
- position: "relative",
1738
- zIndex: 1,
1739
- transform: isSubMenuOpen ? "rotate(180deg)" : void 0,
1740
- transition: "transform 0.2s"
1741
- }
1742
- )
1743
- ] }),
1693
+ effectiveHref ? /* @__PURE__ */ jsxRuntime.jsxs(
1694
+ react.Flex,
1695
+ {
1696
+ align: "center",
1697
+ px: "4",
1698
+ py: "2.5",
1699
+ mx: "4",
1700
+ w: "auto",
1701
+ borderTopRadius: "lg",
1702
+ borderBottomRadius: isSubMenuOpen ? 0 : "lg",
1703
+ role: "group",
1704
+ cursor: isDisabled ? "not-allowed" : "pointer",
1705
+ opacity: 1,
1706
+ pointerEvents: isDisabled ? "none" : "auto",
1707
+ "data-nav-item-active": String(isParentActive),
1708
+ bg: isDisabled ? disabledRowBg : expandedRowBg,
1709
+ color: isDisabled ? disabledRowTextColor : expandedRowTextColor,
1710
+ _hover: {
1711
+ bg: isDisabled ? disabledRowBg : isParentActive ? modeConfig?.accentColor || "brand.600" : "gray.200",
1712
+ color: isDisabled ? disabledRowTextColor : isParentActive ? "white" : "gray.900"
1713
+ },
1714
+ "aria-disabled": isDisabled ? "true" : "false",
1715
+ onClick: (e) => {
1716
+ if (isDisabled) return;
1717
+ e.preventDefault();
1718
+ e.stopPropagation();
1719
+ onToggle?.(menuKey || null);
1720
+ },
1721
+ ...rest,
1722
+ children: [
1723
+ renderIcon(isParentActive),
1724
+ /* @__PURE__ */ jsxRuntime.jsx(react.Box, { flex: "1", children }),
1725
+ /* @__PURE__ */ jsxRuntime.jsx(
1726
+ react.Box,
1727
+ {
1728
+ as: fi.FiChevronDown,
1729
+ transform: isSubMenuOpen ? "rotate(180deg)" : void 0,
1730
+ transition: "transform 0.2s"
1731
+ }
1732
+ )
1733
+ ]
1734
+ }
1735
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
1736
+ react.Flex,
1737
+ {
1738
+ align: "center",
1739
+ px: "4",
1740
+ py: "2.5",
1741
+ mx: "4",
1742
+ w: "auto",
1743
+ borderTopRadius: "lg",
1744
+ borderBottomRadius: isSubMenuOpen ? 0 : "lg",
1745
+ role: "group",
1746
+ cursor: isDisabled ? "not-allowed" : "pointer",
1747
+ opacity: 1,
1748
+ pointerEvents: isDisabled ? "none" : "auto",
1749
+ "data-nav-item-active": String(isParentActive),
1750
+ bg: isDisabled ? disabledRowBg : expandedRowBg,
1751
+ color: isDisabled ? disabledRowTextColor : expandedRowTextColor,
1752
+ _hover: {
1753
+ bg: isDisabled ? disabledRowBg : hasActiveChild ? modeConfig?.accentColor || "brand.600" : "gray.200",
1754
+ color: isDisabled ? disabledRowTextColor : hasActiveChild ? "white" : "gray.900"
1755
+ },
1756
+ "aria-disabled": isDisabled ? "true" : "false",
1757
+ onClick: (e) => {
1758
+ if (isDisabled) return;
1759
+ e.stopPropagation();
1760
+ onToggle?.(menuKey || null);
1761
+ },
1762
+ ...rest,
1763
+ children: [
1764
+ renderIcon(hasActiveChild),
1765
+ /* @__PURE__ */ jsxRuntime.jsx(react.Box, { flex: "1", children }),
1766
+ /* @__PURE__ */ jsxRuntime.jsx(
1767
+ react.Box,
1768
+ {
1769
+ as: fi.FiChevronDown,
1770
+ transform: isSubMenuOpen ? "rotate(180deg)" : void 0,
1771
+ transition: "transform 0.2s"
1772
+ }
1773
+ )
1774
+ ]
1775
+ }
1776
+ ),
1744
1777
  /* @__PURE__ */ jsxRuntime.jsx(react.Collapse, { in: isSubMenuOpen, animateOpacity: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1745
1778
  react.Box,
1746
1779
  {
@@ -1784,74 +1817,68 @@ function NavItem({
1784
1817
  navigate(effectiveHref);
1785
1818
  }
1786
1819
  };
1787
- return /* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "a", href: effectiveHref || "#", onClick: handleClick, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1788
- react.Flex,
1820
+ const rowActive = isSubItem ? isActive : isParentActive;
1821
+ return /* @__PURE__ */ jsxRuntime.jsx(
1822
+ react.Box,
1789
1823
  {
1790
- align: "center",
1791
- px: "4",
1792
- py: "2.5",
1793
- mx: isSubItem ? "0" : "4",
1794
- w: isSubItem ? "full" : "auto",
1795
- borderRadius: isSubItem ? "0" : "lg",
1796
- role: "group",
1797
- cursor: isDisabled ? "not-allowed" : "pointer",
1798
- opacity: 1,
1799
- pointerEvents: isDisabled ? "none" : "auto",
1800
- "data-nav-item-active": String(isActive),
1801
- position: "relative",
1802
- overflow: "hidden",
1803
- bg: isDisabled ? disabledRowBg : isActive ? isSubItem ? "gray.100" : modeConfig?.primaryColor || "brand.500" : "transparent",
1804
- color: isDisabled ? disabledRowTextColor : isActive ? isSubItem ? modeConfig?.primaryColor || "brand.500" : "white" : "gray.600",
1805
- _before: isDisabled ? void 0 : gradientSweepBefore,
1806
- _hover: {
1807
- color: isDisabled ? disabledRowTextColor : "white",
1808
- _before: isDisabled ? void 0 : { width: "100%" }
1809
- },
1810
- "aria-disabled": isDisabled ? "true" : "false",
1824
+ as: "a",
1825
+ href: safeHref(effectiveHref),
1811
1826
  onClick: handleClick,
1812
- children: [
1813
- brandIcon ? /* @__PURE__ */ jsxRuntime.jsx(
1814
- react.Box,
1815
- {
1816
- mr: "4",
1817
- display: "inline-flex",
1818
- alignItems: "center",
1819
- position: "relative",
1820
- zIndex: 1,
1821
- filter: isActive && !isSubItem ? "brightness(0) invert(1)" : void 0,
1822
- _groupHover: isDisabled ? void 0 : { filter: "brightness(0) invert(1)" },
1823
- children: /* @__PURE__ */ jsxRuntime.jsx(BrandIcon_default, { name: brandIcon, size: 18 })
1824
- }
1825
- ) : icon ? /* @__PURE__ */ jsxRuntime.jsx(
1826
- react.Box,
1827
- {
1828
- mr: "4",
1829
- fontSize: "16",
1830
- position: "relative",
1831
- zIndex: 1,
1832
- _groupHover: {
1833
- color: isDisabled ? disabledRowTextColor : "white"
1834
- },
1835
- as: icon
1836
- }
1837
- ) : null,
1838
- /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { flex: "1", position: "relative", zIndex: 1, children: [
1839
- /* @__PURE__ */ jsxRuntime.jsx(react.Box, { children }),
1840
- subLabel ? /* @__PURE__ */ jsxRuntime.jsx(
1841
- react.Text,
1842
- {
1843
- mt: 0.5,
1844
- fontSize: "10px",
1845
- lineHeight: "1.3",
1846
- color: isDisabled ? "gray.400" : "gray.400",
1847
- children: subLabel
1848
- }
1849
- ) : null,
1850
- badge && /* @__PURE__ */ jsxRuntime.jsx(react.Badge, { colorScheme: "orange", size: "xs", mt: 1, fontSize: "xs", display: "block", children: badge })
1851
- ] })
1852
- ]
1827
+ style: { textDecoration: "none" },
1828
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1829
+ react.Flex,
1830
+ {
1831
+ align: "center",
1832
+ px: "4",
1833
+ py: "2.5",
1834
+ mx: isSubItem ? "0" : "4",
1835
+ w: isSubItem ? "full" : "auto",
1836
+ borderRadius: isSubItem ? "0" : "lg",
1837
+ role: "group",
1838
+ cursor: isDisabled ? "not-allowed" : "pointer",
1839
+ opacity: 1,
1840
+ pointerEvents: isDisabled ? "none" : "auto",
1841
+ "data-nav-item-active": String(rowActive),
1842
+ bg: isDisabled ? disabledRowBg : rowActive ? isSubItem ? "gray.100" : modeConfig?.primaryColor || "brand.500" : "transparent",
1843
+ color: isDisabled ? disabledRowTextColor : rowActive ? isSubItem ? modeConfig?.primaryColor || "brand.500" : "white" : "gray.600",
1844
+ _hover: {
1845
+ bg: isDisabled ? disabledRowBg : rowActive ? isSubItem ? "gray.200" : modeConfig?.accentColor || "brand.600" : "gray.100",
1846
+ color: isDisabled ? disabledRowTextColor : rowActive ? isSubItem ? modeConfig?.primaryColor || "brand.500" : "white" : "gray.900"
1847
+ },
1848
+ "aria-disabled": isDisabled ? "true" : "false",
1849
+ onClick: handleClick,
1850
+ ...rest,
1851
+ children: [
1852
+ brandIcon ? /* @__PURE__ */ jsxRuntime.jsx(
1853
+ react.Box,
1854
+ {
1855
+ mr: "4",
1856
+ display: "inline-flex",
1857
+ alignItems: "center",
1858
+ filter: rowActive && !isSubItem ? "brightness(0) invert(1)" : void 0,
1859
+ children: /* @__PURE__ */ jsxRuntime.jsx(BrandIcon_default, { name: brandIcon, size: 18 })
1860
+ }
1861
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx(
1862
+ react.Box,
1863
+ {
1864
+ mr: "4",
1865
+ fontSize: "16",
1866
+ _groupHover: {
1867
+ color: isDisabled ? disabledRowTextColor : rowActive ? isSubItem ? modeConfig?.primaryColor || "brand.500" : "white" : "gray.900"
1868
+ },
1869
+ as: icon
1870
+ }
1871
+ ) : null,
1872
+ /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { flex: "1", children: [
1873
+ /* @__PURE__ */ jsxRuntime.jsx(react.Box, { children }),
1874
+ subLabel ? /* @__PURE__ */ jsxRuntime.jsx(react.Text, { mt: 0.5, fontSize: "10px", lineHeight: "1.3", color: "gray.400", children: subLabel }) : null,
1875
+ badge && /* @__PURE__ */ jsxRuntime.jsx(react.Badge, { colorScheme: "orange", size: "xs", mt: 1, fontSize: "xs", display: "block", children: badge })
1876
+ ] })
1877
+ ]
1878
+ }
1879
+ )
1853
1880
  }
1854
- ) });
1881
+ );
1855
1882
  }
1856
1883
  var NavItem_default = NavItem;
1857
1884