@marimo-team/islands 0.23.15-dev65 → 0.23.15-dev67

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.
@@ -9,7 +9,7 @@ import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
9
9
  import { dt as kioskModeAtom, ht as outputIsStale } from "./html-to-image-DDENewl2.js";
10
10
  import "./chunk-5FQGJX7Z-B9WoXK1R.js";
11
11
  import { u as createLucideIcon } from "./dist-B96tMAst.js";
12
- import { a as DEFAULT_DECK_VERTICAL_ALIGN, c as SlideSidebar, cn as Expand, ct as Panel, dn as Code, i as DEFAULT_DECK_TRANSITION, l as Slide, lt as PanelGroup, o as DEFAULT_SLIDE_TYPE, sn as EyeOff, t as useNotebookCodeAvailable, ut as PanelResizeHandle } from "./code-visibility-CSV-51WI.js";
12
+ import { a as DEFAULT_DECK_VERTICAL_ALIGN, c as SlideSidebar, cn as Expand, ct as Panel, dn as Code, i as DEFAULT_DECK_TRANSITION, l as Slide, lt as PanelGroup, o as DEFAULT_SLIDE_TYPE, sn as EyeOff, t as useNotebookCodeAvailable, ut as PanelResizeHandle } from "./code-visibility-Djd5bgXb.js";
13
13
  import { X as useDebouncedCallback } from "./input-DStQIuqD.js";
14
14
  import "./toDate-B3IRfHhw.js";
15
15
  import "./react-dom-BTJzcVJ9.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.15-dev65",
3
+ "version": "0.23.15-dev67",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -3,6 +3,7 @@ import type { JSX, PropsWithChildren } from "react";
3
3
  import React from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import { z } from "zod";
6
+ import { withFullScreenAsRoot } from "@/components/ui/fullscreen";
6
7
  import {
7
8
  NavigationMenu,
8
9
  NavigationMenuContent,
@@ -68,11 +69,13 @@ const HorizontalMenuGroup = ({
68
69
  const triggerRef = React.useRef<React.ComponentRef<
69
70
  typeof NavigationMenuTrigger
70
71
  > | null>(null);
72
+ const [open, setOpen] = React.useState(false);
71
73
 
72
74
  return (
73
75
  <NavigationMenuPrimitive.Root
74
76
  className="relative z-10 max-w-max flex-1 items-center justify-center"
75
77
  orientation="horizontal"
78
+ onValueChange={(value) => setOpen(value !== "")}
76
79
  >
77
80
  <NavigationMenuList>
78
81
  <NavigationMenuItem>
@@ -96,20 +99,37 @@ const HorizontalMenuGroup = ({
96
99
  </NavigationMenuContent>
97
100
  </NavigationMenuItem>
98
101
  </NavigationMenuList>
99
- <NavigationMenuViewportPortal anchorRef={triggerRef}>
102
+ <NavigationMenuViewportPortal anchorRef={triggerRef} open={open}>
100
103
  <NavigationMenuViewport />
101
104
  </NavigationMenuViewportPortal>
102
105
  </NavigationMenuPrimitive.Root>
103
106
  );
104
107
  };
105
108
 
109
+ // Portal into the fullscreen element (or VSCode output container) when
110
+ // present; `position: fixed` elements portaled to document.body render
111
+ // behind a fullscreened cell.
112
+ const NavigationMenuPortal = withFullScreenAsRoot(
113
+ ({
114
+ container,
115
+ children,
116
+ }: PropsWithChildren<{
117
+ container?: Element | DocumentFragment | null;
118
+ }>) => createPortal(children, container ?? document.body),
119
+ );
120
+
121
+ const VIEWPORT_MARGIN = 8;
122
+
106
123
  const NavigationMenuViewportPortal = ({
107
124
  anchorRef,
125
+ open,
108
126
  children,
109
127
  }: PropsWithChildren<{
110
128
  anchorRef: React.RefObject<HTMLElement | null>;
129
+ open: boolean;
111
130
  }>): React.ReactElement | null => {
112
131
  const [position, setPosition] = React.useState<PortalPosition | null>(null);
132
+ const contentRef = React.useRef<HTMLDivElement | null>(null);
113
133
 
114
134
  const updatePosition = React.useCallback(() => {
115
135
  if (!anchorRef.current) {
@@ -117,15 +137,26 @@ const NavigationMenuViewportPortal = ({
117
137
  }
118
138
 
119
139
  const rect = anchorRef.current.getBoundingClientRect();
140
+ // Clamp so a wide dropdown doesn't run off the right edge of the viewport.
141
+ const contentWidth =
142
+ contentRef.current?.firstElementChild?.scrollWidth ?? 0;
143
+ const maxLeft = window.innerWidth - contentWidth - VIEWPORT_MARGIN;
120
144
  setPosition({
121
- left: rect.left,
145
+ left: Math.max(VIEWPORT_MARGIN, Math.min(rect.left, maxLeft)),
122
146
  top: rect.bottom,
123
147
  });
124
148
  }, [anchorRef]);
125
149
 
150
+ // Recompute whenever the menu opens: the trigger may have moved since mount
151
+ // due to layout shifts that emit no scroll/resize events. The rAF re-measures
152
+ // after the dropdown content has rendered, so clamping sees its real width.
126
153
  React.useLayoutEffect(() => {
127
154
  updatePosition();
128
- }, [updatePosition]);
155
+ if (open) {
156
+ const raf = requestAnimationFrame(updatePosition);
157
+ return () => cancelAnimationFrame(raf);
158
+ }
159
+ }, [open, updatePosition]);
129
160
 
130
161
  useResizeObserver({
131
162
  ref: anchorRef,
@@ -150,14 +181,16 @@ const NavigationMenuViewportPortal = ({
150
181
  return null;
151
182
  }
152
183
 
153
- return createPortal(
154
- <div
155
- className="fixed z-50"
156
- style={{ left: position.left, top: position.top }}
157
- >
158
- {children}
159
- </div>,
160
- document.body,
184
+ return (
185
+ <NavigationMenuPortal>
186
+ <div
187
+ ref={contentRef}
188
+ className="fixed z-50"
189
+ style={{ left: position.left, top: position.top }}
190
+ >
191
+ {children}
192
+ </div>
193
+ </NavigationMenuPortal>
161
194
  );
162
195
  };
163
196
 
@@ -301,23 +334,25 @@ const ListItem = React.forwardRef<
301
334
  >(({ className, label, children, ...props }, ref) => {
302
335
  return (
303
336
  <li>
304
- <a
305
- ref={ref}
306
- className={cn(
307
- "block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-hidden transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
308
- className,
309
- )}
310
- {...props}
311
- >
312
- <div className="text-base font-medium leading-none">
313
- {renderHTML({ html: label })}
314
- </div>
315
- {children && (
316
- <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
317
- {children}
318
- </p>
319
- )}
320
- </a>
337
+ <NavigationMenuLink asChild={true}>
338
+ <a
339
+ ref={ref}
340
+ className={cn(
341
+ "block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-hidden transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
342
+ className,
343
+ )}
344
+ {...props}
345
+ >
346
+ <div className="text-base font-medium leading-none">
347
+ {renderHTML({ html: label })}
348
+ </div>
349
+ {children && (
350
+ <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
351
+ {children}
352
+ </p>
353
+ )}
354
+ </a>
355
+ </NavigationMenuLink>
321
356
  </li>
322
357
  );
323
358
  });