mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
Added loading skeleton to components (#328)
* added sign-in skeleton * added team switcher skeleton * improved style
This commit is contained in:
parent
7cbe56c260
commit
92f7b60ec6
@ -1,3 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null;
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
@ -5,7 +5,5 @@ import { StackServerApp } from "@stackframe/stack";
|
||||
export const stackServerApp = new StackServerApp({
|
||||
tokenStore: "nextjs-cookie",
|
||||
urls: {
|
||||
signIn: "/signin",
|
||||
signUp: "/signup",
|
||||
}
|
||||
});
|
||||
|
||||
@ -3,64 +3,16 @@
|
||||
import React from "react";
|
||||
import { cn } from "../..";
|
||||
|
||||
// TODO: add this to the generated global CSS
|
||||
const styleSheet = `
|
||||
@keyframes animation-light {
|
||||
0% {
|
||||
filter: grayscale(1) contrast(0) brightness(0) invert(1) brightness(0.8);
|
||||
function Skeleton({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={cn("animate-pulse rounded-md bg-primary/10", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
100% {
|
||||
filter: grayscale(1) contrast(0) brightness(0) invert(1) brightness(0.9);
|
||||
}
|
||||
|
||||
@keyframes animation-dark {
|
||||
0% {
|
||||
filter: grayscale(1) contrast(0) brightness(0) invert(1) brightness(0.2);
|
||||
}
|
||||
100% {
|
||||
filter: grayscale(1) contrast(0) brightness(0) invert(1) brightness(0.1);
|
||||
}
|
||||
|
||||
.stack-skeleton[data-stack-state="activated"],
|
||||
.stack-skeleton[data-stack-state="activated"] * {
|
||||
pointer-events: none !important;
|
||||
-webkit-user-select: none !important;
|
||||
-moz-user-select: none !important;
|
||||
user-select: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.stack-skeleton[data-stack-state="activated"] {
|
||||
animation: animation-light 1s infinite alternate-reverse !important;
|
||||
}
|
||||
|
||||
html[data-stack-theme='dark'] .stack-skeleton[data-stack-state="activated"] {
|
||||
animation: animation-dark 1s infinite alternate-reverse !important;
|
||||
}
|
||||
|
||||
html[data-stack-theme='dark'] .stack-skeleton[data-stack-state="activated"] {
|
||||
animation: animation-dark 1s infinite alternate-reverse !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const Skeleton = React.forwardRef<
|
||||
React.ElementRef<"span">,
|
||||
React.ComponentPropsWithoutRef<"span"> & { deactivated?: boolean }
|
||||
>(
|
||||
(props, ref) => {
|
||||
return (
|
||||
<>
|
||||
<style>{styleSheet}</style>
|
||||
<span
|
||||
{...props}
|
||||
ref={ref}
|
||||
data-stack-state={props.deactivated ? "deactivated" : "activated"}
|
||||
className={cn(props.className, "stack-skeleton")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
Skeleton.displayName = "Skeleton";
|
||||
|
||||
export { Skeleton };
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { runAsynchronously } from '@stackframe/stack-shared/dist/utils/promises';
|
||||
import { StyledLink, Tabs, TabsContent, TabsList, TabsTrigger, Typography, cn } from '@stackframe/stack-ui';
|
||||
import { useEffect } from 'react';
|
||||
import { Skeleton, StyledLink, Tabs, TabsContent, TabsList, TabsTrigger, Typography, cn } from '@stackframe/stack-ui';
|
||||
import { Suspense, useEffect } from 'react';
|
||||
import { useStackApp, useUser } from '..';
|
||||
import { CredentialSignIn } from '../components/credential-sign-in';
|
||||
import { CredentialSignUp } from '../components/credential-sign-up';
|
||||
@ -11,10 +11,10 @@ import { SeparatorWithText } from '../components/elements/separator-with-text';
|
||||
import { MagicLinkSignIn } from '../components/magic-link-sign-in';
|
||||
import { PredefinedMessageCard } from '../components/message-cards/predefined-message-card';
|
||||
import { OAuthButtonGroup } from '../components/oauth-button-group';
|
||||
import { useTranslation } from '../lib/translations';
|
||||
import { PasskeyButton } from '../components/passkey-button';
|
||||
import { useTranslation } from '../lib/translations';
|
||||
|
||||
export function AuthPage(props: {
|
||||
type Props = {
|
||||
noPasswordRepeat?: boolean,
|
||||
firstTab?: 'magic-link' | 'password',
|
||||
fullPage?: boolean,
|
||||
@ -32,7 +32,37 @@ export function AuthPage(props: {
|
||||
}[],
|
||||
},
|
||||
},
|
||||
}) {
|
||||
}
|
||||
|
||||
export function AuthPage(props: Props) {
|
||||
return <Suspense fallback={<Fallback {...props} />}>
|
||||
<Inner {...props} />
|
||||
</Suspense>;
|
||||
}
|
||||
|
||||
function Fallback(props: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<MaybeFullPage fullPage={!!props.fullPage}>
|
||||
<div className='stack-scope flex flex-col items-stretch' style={{ maxWidth: '380px', flexBasis: '380px', padding: props.fullPage ? '1rem' : 0 }}>
|
||||
<div className="text-center mb-6 flex flex-col">
|
||||
<Skeleton className='h-9 w-2/3 self-center' />
|
||||
|
||||
<Skeleton className='h-3 w-16 mt-8' />
|
||||
<Skeleton className='h-9 w-full mt-1' />
|
||||
|
||||
<Skeleton className='h-3 w-24 mt-2' />
|
||||
<Skeleton className='h-9 w-full mt-1' />
|
||||
|
||||
<Skeleton className='h-9 w-full mt-6' />
|
||||
</div>
|
||||
</div>
|
||||
</MaybeFullPage>
|
||||
);
|
||||
}
|
||||
|
||||
function Inner (props: Props) {
|
||||
const stackApp = useStackApp();
|
||||
const user = useUser();
|
||||
const projectFromHook = stackApp.useProject();
|
||||
|
||||
@ -10,11 +10,12 @@ import {
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Skeleton,
|
||||
Typography
|
||||
} from "@stackframe/stack-ui";
|
||||
import { PlusCircle, Settings } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { Suspense, useEffect, useMemo } from "react";
|
||||
import { Team, useStackApp, useUser } from "..";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { TeamIcon } from "./team-icon";
|
||||
@ -26,6 +27,16 @@ type SelectedTeamSwitcherProps = {
|
||||
};
|
||||
|
||||
export function SelectedTeamSwitcher(props: SelectedTeamSwitcherProps) {
|
||||
return <Suspense fallback={<Fallback />}>
|
||||
<Inner {...props} />
|
||||
</Suspense>;
|
||||
}
|
||||
|
||||
function Fallback() {
|
||||
return <Skeleton className="h-9 w-full max-w-64 stack-scope" />;
|
||||
}
|
||||
|
||||
function Inner(props: SelectedTeamSwitcherProps) {
|
||||
const { t } = useTranslation();
|
||||
const app = useStackApp();
|
||||
const user = useUser();
|
||||
|
||||
@ -31,13 +31,7 @@ type UserButtonProps = {
|
||||
|
||||
export function UserButton(props: UserButtonProps) {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<Skeleton>
|
||||
<UserButtonInnerInner {...props} user={null} />
|
||||
</Skeleton>
|
||||
}
|
||||
>
|
||||
<Suspense fallback={<Skeleton className="h-[34px] w-[34px] rounded-full stack-scope" />}>
|
||||
<UserButtonInner {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user