Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/demo/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StackProvider, StackTheme } from "@stackframe/stack";
import { DemoFloatingWindow, StackProvider, StackTheme } from "@stackframe/stack";
import { Metadata } from "next";
import React from "react";
import Header from "src/components/header";
Expand All @@ -23,6 +23,7 @@ export default function RootLayout({
<StackProvider app={stackServerApp}>
<StackTheme>
<Provider>
<DemoFloatingWindow />
<div className="flex flex-col h-screen">
<Header />
<div className="flex flex-grow">
Expand Down
14 changes: 2 additions & 12 deletions packages/init-stack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ async function getUpdatedLayout(originalLayout: string): Promise<LayoutResult |
const importInsertLocationM1 =
firstImportLocationM1 ?? (hasStringAsFirstLine ? layout.indexOf("\n") : -1);
const importInsertLocation = importInsertLocationM1 + 1;
const importStatement = `import { StackProvider, StackTheme } from "@stackframe/stack";\nimport { stackServerApp } from "../stack";\n`;
const importStatement = `import { StackProvider, StackTheme, DemoFloatingWindow } from "@stackframe/stack";\nimport { stackServerApp } from "../stack";\n`;
layout =
layout.slice(0, importInsertLocation) +
importStatement +
Expand All @@ -751,17 +751,7 @@ async function getUpdatedLayout(originalLayout: string): Promise<LayoutResult |
return undefined;
}

const lines = layout.split("\n");
const [bodyOpenEndLine, bodyOpenEndIndexInLine] = getLineIndex(
lines,
bodyOpenEndIndex
);
const [bodyCloseStartLine, bodyCloseStartIndexInLine] = getLineIndex(
lines,
bodyCloseStartIndex
);

const insertOpen = "<StackProvider app={stackServerApp}><StackTheme>";
const insertOpen = "<StackProvider app={stackServerApp}><StackTheme><DemoFloatingWindow />";
const insertClose = "</StackTheme></StackProvider>";

layout =
Expand Down
156 changes: 156 additions & 0 deletions packages/template/src/components/demo-floating-window.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"use client";

import { Skeleton, Typography, buttonVariants, cn } from "@stackframe/stack-ui";
import { GripHorizontal } from "lucide-react";
import { Suspense, useEffect, useRef, useState } from "react";
import { UserAvatar, useStackApp, useUser } from "..";
import { Link } from "./link";

type Position = {
x: number,
y: number,
};

function DemoFloatingWindowContent() {
const user = useUser();
const app = useStackApp();
const buttonClass = cn("w-full", buttonVariants({ variant: "outline" }));

return (
<div className="mt-4 flex flex-col gap-2">
{user ? (
<>
<div className="flex items-center gap-2 mb-2">
<UserAvatar />
<div className="flex flex-col">
<p className="text-sm font-medium">{user.displayName}</p>
<p className="text-xs text-muted-foreground">{user.primaryEmail}</p>
</div>
</div>

<Link
className={buttonClass}
href={app.urls.accountSettings}
>
Account Settings
</Link>
<Link
className={buttonClass}
href={app.urls.signOut}
>
Sign Out
</Link>
</>
) : (
<>
<Link
className={buttonClass}
href={app.urls.signIn}
>
Sign In
</Link>
<Link
className={buttonClass}
href={app.urls.signUp}
>
Sign Up
</Link>
</>
)}
</div>
);
}

function DemoFloatingWindowSkeleton() {
return (
<div className="mt-4 flex flex-col gap-2">
<Skeleton className="h-9 w-full" />
<Skeleton className="h-9 w-full" />
</div>
);
}

export function DemoFloatingWindow() {
const [position, setPosition] = useState<Position>({ x: 20, y: 20 });
const [isDragging, setIsDragging] = useState(false);
const [dragOffset, setDragOffset] = useState<Position>({ x: 0, y: 0 });
const windowRef = useRef<HTMLDivElement>(null);

if (process.env.NODE_ENV !== "development") {
return null;
}

const handlePointerDown = (e: React.PointerEvent) => {
Comment thread
fomalhautb marked this conversation as resolved.
// Prevent text selection during drag
e.preventDefault();

if (windowRef.current) {
const rect = windowRef.current.getBoundingClientRect();
setDragOffset({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
setIsDragging(true);
}
};

useEffect(() => {
Comment thread
fomalhautb marked this conversation as resolved.
const handlePointerMove = (e: PointerEvent) => {
if (isDragging) {
setPosition({
x: e.clientX - dragOffset.x,
y: e.clientY - dragOffset.y,
});
}
};

const handlePointerUp = () => {
setIsDragging(false);
};

if (isDragging) {
document.addEventListener("pointermove", handlePointerMove);
document.addEventListener("pointerup", handlePointerUp);
document.addEventListener("pointercancel", handlePointerUp);
}

return () => {
document.removeEventListener("pointermove", handlePointerMove);
document.removeEventListener("pointerup", handlePointerUp);
document.removeEventListener("pointercancel", handlePointerUp);
};
}, [isDragging, dragOffset]);

return (
<div
ref={windowRef}
className={cn(
"stack-scope fixed flex flex-col gap-3 p-5 rounded-lg shadow-lg bg-background/50 backdrop-blur-sm border border-border w-64",
isDragging && "cursor-grabbing"
)}
style={{
left: `${position.x}px`,
top: `${position.y}px`,
zIndex: 1000,
}}
Comment thread
fomalhautb marked this conversation as resolved.
>
<div
Comment thread
fomalhautb marked this conversation as resolved.
className="absolute top-0 left-0 right-0 h-6 bg-muted/70 rounded-t-lg cursor-grab flex items-center justify-center"
onPointerDown={handlePointerDown}
style={{ touchAction: "none" }}
>
<GripHorizontal className="w-4 h-4" />
</div>

<Suspense fallback={<DemoFloatingWindowSkeleton />}>
<DemoFloatingWindowContent />
</Suspense>

<div className="flex justify-center text-center">
<Typography type='footnote' variant='secondary'>
This is only visible in dev. Remove in layout.tsx.
</Typography>
</div>
</div>
);
}
4 changes: 3 additions & 1 deletion packages/template/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { StackTheme } from './providers/theme-provider';

export { AccountSettings } from "./components-page/account-settings";
export { AuthPage } from "./components-page/auth-page";
export { CliAuthConfirmation } from "./components-page/cli-auth-confirm";
export { EmailVerification } from "./components-page/email-verification";
export { ForgotPassword } from "./components-page/forgot-password";
export { PasswordReset } from "./components-page/password-reset";
Expand All @@ -22,5 +23,6 @@ export { OAuthButton } from "./components/oauth-button";
export { OAuthButtonGroup } from "./components/oauth-button-group";
export { SelectedTeamSwitcher } from "./components/selected-team-switcher";
export { UserButton } from "./components/user-button";
export { CliAuthConfirmation } from "./components-page/cli-auth-confirm";

export { DemoFloatingWindow } from "./components/demo-floating-window";
// END_PLATFORM