feat(ui): move preview device toggles into the global nav rail
This commit is contained in:
@@ -5,6 +5,7 @@ import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useAnatomy } from "@/components/project/use-anatomy";
|
||||
import { usePreviewBridge } from "@/components/project/preview-bridge-context";
|
||||
import { usePreviewToolbarStore } from "@/components/project/preview-toolbar/preview-toolbar-state";
|
||||
|
||||
const SAME_ORIGIN_SANDBOX =
|
||||
"allow-scripts allow-forms allow-same-origin allow-popups allow-modals allow-downloads" as const;
|
||||
@@ -32,7 +33,7 @@ export default function PreviewTab() {
|
||||
const bridge = usePreviewBridge();
|
||||
const origin = typeof window !== "undefined" ? window.location.origin : "";
|
||||
|
||||
const [deviceMode, setDeviceMode] = useState<"desktop" | "mobile">("desktop");
|
||||
const deviceMode = usePreviewToolbarStore((s) => s.deviceMode);
|
||||
|
||||
// Auto-select first preview on load
|
||||
useEffect(() => {
|
||||
@@ -67,43 +68,6 @@ export default function PreviewTab() {
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
background: "rgba(255,255,255,0.85)",
|
||||
padding: 4,
|
||||
borderRadius: 8,
|
||||
border: "1px solid rgba(26,26,26,0.12)",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setDeviceMode("desktop")}
|
||||
style={{
|
||||
padding: "4px 12px",
|
||||
borderRadius: 6,
|
||||
fontSize: "0.8rem",
|
||||
background:
|
||||
deviceMode === "desktop" ? "rgba(0,0,0,0.06)" : "transparent",
|
||||
color: deviceMode === "desktop" ? "#000" : "#666",
|
||||
}}
|
||||
>
|
||||
Desktop
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setDeviceMode("mobile")}
|
||||
style={{
|
||||
padding: "4px 12px",
|
||||
borderRadius: 6,
|
||||
fontSize: "0.8rem",
|
||||
background:
|
||||
deviceMode === "mobile" ? "rgba(0,0,0,0.06)" : "transparent",
|
||||
color: deviceMode === "mobile" ? "#000" : "#666",
|
||||
}}
|
||||
>
|
||||
Mobile
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
||||
11
components/project/preview-toolbar/preview-toolbar-state.ts
Normal file
11
components/project/preview-toolbar/preview-toolbar-state.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface PreviewToolbarState {
|
||||
deviceMode: 'desktop' | 'mobile';
|
||||
setDeviceMode: (mode: 'desktop' | 'mobile') => void;
|
||||
}
|
||||
|
||||
export const usePreviewToolbarStore = create<PreviewToolbarState>((set) => ({
|
||||
deviceMode: 'desktop',
|
||||
setDeviceMode: (mode) => set({ deviceMode: mode }),
|
||||
}));
|
||||
@@ -46,6 +46,9 @@ const PRIMARY_ITEMS: RailItem[] = [
|
||||
export function ProjectIconRail({ workspace, projectId }: Props) {
|
||||
const pathname = usePathname() ?? "";
|
||||
const projectBase = `/${workspace}/project/${projectId}`;
|
||||
const isPreviewActive =
|
||||
pathname === `${projectBase}/preview` ||
|
||||
pathname.startsWith(`${projectBase}/preview/`);
|
||||
|
||||
const isActive = (item: RailItem) => {
|
||||
const segments = [item.segment, ...(item.aliases ?? [])];
|
||||
@@ -58,7 +61,13 @@ export function ProjectIconRail({ workspace, projectId }: Props) {
|
||||
|
||||
return (
|
||||
<nav style={bar} aria-label="Project sections">
|
||||
{/* Dynamic Left Content Area (e.g. Preview Device Toggles) */}
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
{isPreviewActive && <PreviewDeviceToggles />}
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, minWidth: 0 }} aria-hidden />
|
||||
|
||||
<div style={primaryGroup}>
|
||||
{PRIMARY_ITEMS.map((item) => (
|
||||
<RailLink
|
||||
@@ -83,6 +92,72 @@ export function ProjectIconRail({ workspace, projectId }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
import { Monitor, Smartphone } from "lucide-react";
|
||||
import { usePreviewToolbarStore } from "./preview-toolbar/preview-toolbar-state";
|
||||
|
||||
function PreviewDeviceToggles() {
|
||||
const deviceMode = usePreviewToolbarStore((s) => s.deviceMode);
|
||||
const setDeviceMode = usePreviewToolbarStore((s) => s.setDeviceMode);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: 4,
|
||||
background: "#f1ebe3",
|
||||
padding: 4,
|
||||
borderRadius: 8,
|
||||
border: "1px solid #e8e4dc",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setDeviceMode("desktop")}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
padding: "4px 10px",
|
||||
borderRadius: 6,
|
||||
fontSize: "0.75rem",
|
||||
fontWeight: 500,
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.15s",
|
||||
background: deviceMode === "desktop" ? "#ffffff" : "transparent",
|
||||
color: deviceMode === "desktop" ? "#1a1a1a" : "#8c8580",
|
||||
boxShadow:
|
||||
deviceMode === "desktop" ? "0 1px 2px rgba(0,0,0,0.05)" : "none",
|
||||
}}
|
||||
>
|
||||
<Monitor size={14} />
|
||||
Desktop
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setDeviceMode("mobile")}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
padding: "4px 10px",
|
||||
borderRadius: 6,
|
||||
fontSize: "0.75rem",
|
||||
fontWeight: 500,
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.15s",
|
||||
background: deviceMode === "mobile" ? "#ffffff" : "transparent",
|
||||
color: deviceMode === "mobile" ? "#1a1a1a" : "#8c8580",
|
||||
boxShadow:
|
||||
deviceMode === "mobile" ? "0 1px 2px rgba(0,0,0,0.05)" : "none",
|
||||
}}
|
||||
>
|
||||
<Smartphone size={14} />
|
||||
Mobile
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RailLink({
|
||||
href,
|
||||
label,
|
||||
|
||||
Reference in New Issue
Block a user