@matteoaliano/forest-ui 1.2.3 → 1.2.4

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/index.d.ts CHANGED
@@ -558,6 +558,12 @@ interface AppShellProps {
558
558
  * sticky AppBar (brand / search / actions + the color-mode toggle) over a
559
559
  * SidebarNav and a main content area. Render a <Page> as children.
560
560
  *
561
+ * The AppBar and the sidebar read as one continuous L-shaped chrome — they
562
+ * already share `bg.primary`, so the shell drops the borders that used to cut
563
+ * them apart. The main region is then a lifted surface tucked into the corner
564
+ * they form: rounded at the top-left only, flush with the right and bottom
565
+ * window edges.
566
+ *
561
567
  * Below the "sm" breakpoint the sidebar becomes a temporary overlay drawer
562
568
  * opened by a hamburger button in the AppBar.
563
569
  */
package/dist/index.js CHANGED
@@ -1389,7 +1389,13 @@ function buildComponents(tokens2) {
1389
1389
  borderBottomLeftRadius: radius4.md,
1390
1390
  borderBottomRightRadius: radius4.md,
1391
1391
  opacity: 0.75,
1392
- boxShadow: "inset 0 2px 4px 0 rgba(0,0,0,0.3)",
1392
+ // The pressed-in cue is carried by the flattened bevel (bottom
1393
+ // border 4px -> 1px) alone in light mode. On the dark-mode face
1394
+ // that geometry reads too faintly, so it keeps the inner shadow.
1395
+ boxShadow: "none",
1396
+ ...theme.applyStyles("dark", {
1397
+ boxShadow: "inset 0 2px 4px 0 rgba(0,0,0,0.3)"
1398
+ }),
1393
1399
  "&::before": { display: "none" },
1394
1400
  // Stays pressed on hover — no bevel re-raise, no brightness lift.
1395
1401
  "&:hover": {
@@ -3425,7 +3431,12 @@ var display = {
3425
3431
  var container = {
3426
3432
  maxWidthDesktop: 1280,
3427
3433
  paddingDesktop: 32,
3428
- paddingMobile: 16
3434
+ paddingMobile: 16,
3435
+ // Corner radius of the App Shell's content surface where it meets the
3436
+ // chrome. Deliberately a layout value rather than a step on the `radius`
3437
+ // scale: that scale governs components (and the sharper alkemy+ preset caps
3438
+ // it at 12), so a 24 sitting in it would misread as a component radius.
3439
+ shellRadius: 24
3429
3440
  };
3430
3441
  var widths = {
3431
3442
  xxs: 320,
@@ -5513,7 +5524,13 @@ function SidebarNav({
5513
5524
  // Match the drawer paper's width animation so the absolutely-
5514
5525
  // positioned toggle (pinned to this box's right edge) glides with the
5515
5526
  // sidebar instead of jumping. Constant in hover mode → no-op there.
5516
- transition: "width 200ms ease-in-out"
5527
+ transition: "width 200ms ease-in-out",
5528
+ // A hover-expanded sidebar has to overlay the content, which in
5529
+ // AppShell sits at drawer + 1. This lives on the wrapper rather than
5530
+ // on `& .MuiDrawer-paper` because a consumer's `sx` replaces that
5531
+ // nested key wholesale (AppShell does). Only while expanded, so the
5532
+ // resting rail still takes the content surface's shadow.
5533
+ ...isHover && open && { zIndex: (theme) => theme.zIndex.drawer + 2 }
5517
5534
  },
5518
5535
  children: [
5519
5536
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
@@ -5529,8 +5546,7 @@ function SidebarNav({
5529
5546
  width: drawerWidth,
5530
5547
  transition: "width 200ms ease-in-out",
5531
5548
  overflowX: "hidden",
5532
- boxSizing: "border-box",
5533
- ...isHover && { zIndex: (theme) => theme.zIndex.drawer + 2 }
5549
+ boxSizing: "border-box"
5534
5550
  },
5535
5551
  ...sx
5536
5552
  },
@@ -5545,7 +5561,19 @@ function SidebarNav({
5545
5561
  },
5546
5562
  children: [
5547
5563
  header && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box.default, { sx: { flexShrink: 0 }, children: header }),
5548
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box.default, { sx: { flexGrow: 1, overflowY: "auto", overflowX: "hidden" }, children }),
5564
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5565
+ import_Box.default,
5566
+ {
5567
+ sx: {
5568
+ flexGrow: 1,
5569
+ overflowY: "auto",
5570
+ overflowX: "hidden",
5571
+ px: 1,
5572
+ "& > .MuiList-root": { paddingInline: 0 }
5573
+ },
5574
+ children
5575
+ }
5576
+ ),
5549
5577
  footer && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_Box.default, { sx: { flexShrink: 0 }, children: footer })
5550
5578
  ]
5551
5579
  }
@@ -5566,7 +5594,11 @@ function SidebarNav({
5566
5594
  top: "50%",
5567
5595
  right: 0,
5568
5596
  transform: "translate(50%, -50%)",
5569
- zIndex: theme.zIndex.drawer + 1,
5597
+ // Above both the AppShell content surface (drawer + 1, which
5598
+ // paints its shadow over the sidebar) and the hover-expanded
5599
+ // drawer (drawer + 2) — the toggle straddles the seam and has
5600
+ // to stay clickable and whole on top of both.
5601
+ zIndex: theme.zIndex.drawer + 3,
5570
5602
  minWidth: 0,
5571
5603
  width: 28,
5572
5604
  height: 28,
@@ -5601,6 +5633,10 @@ function SidebarNav({
5601
5633
  var itemSx = {
5602
5634
  // Preset radius.md (4px) — same corner radius as buttons and inputs.
5603
5635
  borderRadius: "4px",
5636
+ // The MuiListItemButton override sets 12px; 8px here absorbs the container's
5637
+ // new 8px gutter so the icon column stays exactly 16px from the sidebar edge,
5638
+ // where it has always been.
5639
+ paddingInline: "8px",
5604
5640
  // 4px gap between stacked items, independent of the container (List/flex).
5605
5641
  marginBottom: "4px",
5606
5642
  "&:last-of-type": { marginBottom: 0 },
@@ -5728,63 +5764,135 @@ function AppShell({
5728
5764
  )) });
5729
5765
  const brandEl = brand != null ? brand : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ShellBrand, { product, logoVariant });
5730
5766
  const wrapLogomark = !brand && logoVariant === "logomark" && !isMobile;
5731
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Box2.default, { sx: { display: "flex", flexDirection: "column", height: "100vh" }, children: [
5732
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5733
- import_AppBar.default,
5767
+ return (
5768
+ // Clipping lives here rather than on the body row below, so the content
5769
+ // surface's shadow can spill up over the AppBar instead of being cut off
5770
+ // at the row's top edge.
5771
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5772
+ import_Box2.default,
5734
5773
  {
5735
- position: "sticky",
5736
- elevation: 0,
5737
- sx: { zIndex: theme.zIndex.drawer + 1 },
5738
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Toolbar.default, { variant: "dense", sx: { px: { xs: "16px", sm: "16px" } }, children: [
5739
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Box2.default, { sx: { flex: 1, display: "flex", alignItems: "center", justifyContent: "flex-start", gap: 1 }, children: [
5740
- isMobile && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5741
- IconButton2,
5774
+ sx: {
5775
+ display: "flex",
5776
+ flexDirection: "column",
5777
+ height: "100vh",
5778
+ overflow: "hidden",
5779
+ // The chrome fill belongs here, not only on the AppBar and Drawer.
5780
+ // `main`'s rounded top-left corner clips its own background, and the
5781
+ // notch between that arc and the square box corner is reached by
5782
+ // neither the bar nor the sidebar — so without this, whatever sits
5783
+ // behind the shell shows through it. Storybook's docs container is
5784
+ // white, which is exactly how it surfaced.
5785
+ backgroundColor: "background.default"
5786
+ },
5787
+ children: [
5788
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5789
+ import_AppBar.default,
5790
+ {
5791
+ position: "sticky",
5792
+ elevation: 0,
5793
+ sx: { zIndex: theme.zIndex.drawer + 1, borderBottom: "none" },
5794
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Toolbar.default, { variant: "dense", sx: { px: { xs: "16px", sm: "16px" } }, children: [
5795
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Box2.default, { sx: { flex: 1, display: "flex", alignItems: "center", justifyContent: "flex-start", gap: 1 }, children: [
5796
+ isMobile && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5797
+ IconButton2,
5798
+ {
5799
+ variant: "icon",
5800
+ size: "small",
5801
+ color: "inherit",
5802
+ "aria-label": "Open navigation",
5803
+ onClick: () => setMobileOpen(true),
5804
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_MenuOutlined.default, { fontSize: "small" })
5805
+ }
5806
+ ),
5807
+ wrapLogomark ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_Box2.default, { sx: { width: 48, ml: "-16px", display: "flex", justifyContent: "center" }, children: brandEl }) : brandEl
5808
+ ] }),
5809
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_Box2.default, { sx: { flex: 1, display: "flex", justifyContent: "center" }, children: search === true ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Search, { size: "small", placeholder: "Search\u2026", sx: { width: "100%", maxWidth: 480 } }) : search || null }),
5810
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Box2.default, { sx: { flex: 1, display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 1 }, children: [
5811
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ColorModeToggle, {}),
5812
+ actions
5813
+ ] })
5814
+ ] })
5815
+ }
5816
+ ),
5817
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Box2.default, { sx: { display: "flex", flexGrow: 1, minHeight: 0 }, children: [
5818
+ !isMobile && navContent && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5819
+ SidebarNav,
5742
5820
  {
5743
- variant: "icon",
5744
- size: "small",
5745
- color: "inherit",
5746
- "aria-label": "Open navigation",
5747
- onClick: () => setMobileOpen(true),
5748
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_MenuOutlined.default, { fontSize: "small" })
5821
+ open: sidebarOpen,
5822
+ onOpenChange: setSidebarOpen,
5823
+ behavior: sidebarBehavior,
5824
+ sx: {
5825
+ height: "100%",
5826
+ "& .MuiDrawer-paper": {
5827
+ position: "relative",
5828
+ height: "100%",
5829
+ // Same reason as the AppBar's border: the chrome is one piece.
5830
+ borderRight: "none"
5831
+ }
5832
+ },
5833
+ children: navContent
5749
5834
  }
5750
5835
  ),
5751
- wrapLogomark ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_Box2.default, { sx: { width: 48, ml: "-16px", display: "flex", justifyContent: "center" }, children: brandEl }) : brandEl
5836
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5837
+ import_Box2.default,
5838
+ {
5839
+ component: "main",
5840
+ sx: (t) => ({
5841
+ flexGrow: 1,
5842
+ overflow: "auto",
5843
+ backgroundColor: "background.paper",
5844
+ // Lift the surface above the chrome so its shadow lands ON the
5845
+ // sidebar and the AppBar rather than under them. Level with the
5846
+ // AppBar and later in the DOM, so it wins there; the hover
5847
+ // sidebar sits a level higher still and keeps overlaying content.
5848
+ position: "relative",
5849
+ zIndex: t.zIndex.drawer + 1,
5850
+ // Longhands, not `borderTop: "1px solid"` — the shorthand resets
5851
+ // border-*-color to currentColor and the hairline renders as text
5852
+ // black however `borderColor` is ordered around it.
5853
+ borderStyle: "solid",
5854
+ borderColor: "divider",
5855
+ borderTopWidth: "1px",
5856
+ borderRightWidth: 0,
5857
+ borderBottomWidth: 0,
5858
+ borderTopLeftRadius: `${container.shellRadius}px`,
5859
+ // With no sidebar below "sm" a single left corner reads lopsided,
5860
+ // so the surface rounds both top corners and drops the left edge.
5861
+ borderLeftWidth: { xs: 0, sm: "1px" },
5862
+ borderTopRightRadius: { xs: `${container.shellRadius}px`, sm: 0 },
5863
+ // Up-and-left, so the lift falls onto the chrome. In dark the
5864
+ // surface is already a step lighter than the chrome and does most
5865
+ // of the lifting itself, so the shadow only has to seat the edge.
5866
+ // Wide and low-contrast rather than tight and dark — a diffuse
5867
+ // falloff reads as lift without drawing a line.
5868
+ boxShadow: "-2px -2px 20px -16px #1018281a, -1px -1px 6px -3px #1018281c",
5869
+ // Dark keeps the same `divider` hairline as light — gray-900 on
5870
+ // gray-950 is only a ~15-level tonal step, so the line still does
5871
+ // the work of defining the edge. Only the shadow changes: one
5872
+ // contact layer, since the surface being lighter than the chrome
5873
+ // already reads as raised.
5874
+ ...t.applyStyles("dark", {
5875
+ boxShadow: "-1px -1px 6px -3px #00000059"
5876
+ })
5877
+ }),
5878
+ children
5879
+ }
5880
+ )
5752
5881
  ] }),
5753
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_Box2.default, { sx: { flex: 1, display: "flex", justifyContent: "center" }, children: search === true ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Search, { size: "small", placeholder: "Search\u2026", sx: { width: "100%", maxWidth: 480 } }) : search || null }),
5754
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Box2.default, { sx: { flex: 1, display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 1 }, children: [
5755
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ColorModeToggle, {}),
5756
- actions
5757
- ] })
5758
- ] })
5759
- }
5760
- ),
5761
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_Box2.default, { sx: { display: "flex", flexGrow: 1, overflow: "hidden" }, children: [
5762
- !isMobile && navContent && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5763
- SidebarNav,
5764
- {
5765
- open: sidebarOpen,
5766
- onOpenChange: setSidebarOpen,
5767
- behavior: sidebarBehavior,
5768
- sx: {
5769
- height: "100%",
5770
- "& .MuiDrawer-paper": { position: "relative", height: "100%" }
5771
- },
5772
- children: navContent
5773
- }
5774
- ),
5775
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_Box2.default, { component: "main", sx: { flexGrow: 1, overflow: "auto" }, children })
5776
- ] }),
5777
- isMobile && navContent && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5778
- import_Drawer3.default,
5779
- {
5780
- open: mobileOpen,
5781
- onClose: () => setMobileOpen(false),
5782
- onClick: () => setMobileOpen(false),
5783
- sx: { "& .MuiDrawer-paper": { width: 240 } },
5784
- children: navContent
5882
+ isMobile && navContent && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5883
+ import_Drawer3.default,
5884
+ {
5885
+ open: mobileOpen,
5886
+ onClose: () => setMobileOpen(false),
5887
+ onClick: () => setMobileOpen(false),
5888
+ sx: { "& .MuiDrawer-paper": { width: 240 } },
5889
+ children: navContent
5890
+ }
5891
+ )
5892
+ ]
5785
5893
  }
5786
5894
  )
5787
- ] });
5895
+ );
5788
5896
  }
5789
5897
 
5790
5898
  // src/components/Page/index.tsx