InterstitialLayout.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import { motion } from 'framer-motion'
  2. import { ArrowRightLeft } from 'lucide-react'
  3. import type { PropsWithChildren, ReactNode } from 'react'
  4. import { Card, CardContent, CardHeader, cn } from 'ui'
  5. import { ProfileImage } from '@/components/ui/ProfileImage'
  6. import { BASE_PATH } from '@/lib/constants'
  7. const MotionCard = motion.create(Card)
  8. interface InterstitialLayoutProps {
  9. logo?: ReactNode
  10. title?: ReactNode
  11. description?: ReactNode
  12. containerClassName?: string
  13. cardClassName?: string
  14. titleClassName?: string
  15. descriptionClassName?: string
  16. }
  17. export const InterstitialLayout = ({
  18. logo,
  19. title,
  20. description,
  21. containerClassName,
  22. cardClassName,
  23. titleClassName,
  24. descriptionClassName,
  25. children,
  26. }: PropsWithChildren<InterstitialLayoutProps>) => {
  27. const TitleElement = typeof title === 'string' ? 'h1' : 'div'
  28. const DescriptionElement = typeof description === 'string' ? 'p' : 'div'
  29. return (
  30. <div
  31. className={cn(
  32. 'flex min-h-screen w-full items-center justify-center bg-studio px-2 py-6',
  33. containerClassName
  34. )}
  35. >
  36. <MotionCard
  37. layout="size"
  38. transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
  39. className={cn('mx-auto w-full max-w-[400px] overflow-hidden', cardClassName)}
  40. >
  41. {(logo || title || description) && (
  42. <CardHeader className="items-center gap-0 space-y-0 border-0 px-6 py-6 text-center font-normal [--card-padding-x:1.5rem]">
  43. {logo && <div className="mb-4 flex justify-center">{logo}</div>}
  44. {(title || description) && (
  45. <div className="flex flex-col items-center gap-1">
  46. {title && (
  47. <TitleElement
  48. className={cn(
  49. 'font-sans text-lg font-medium tracking-tight text-balance text-foreground',
  50. titleClassName
  51. )}
  52. >
  53. {title}
  54. </TitleElement>
  55. )}
  56. {description && (
  57. <DescriptionElement
  58. className={cn(
  59. '!m-0 px-3 text-sm leading-tight !text-balance text-foreground-lighter',
  60. descriptionClassName
  61. )}
  62. >
  63. {description}
  64. </DescriptionElement>
  65. )}
  66. </div>
  67. )}
  68. </CardHeader>
  69. )}
  70. {children}
  71. </MotionCard>
  72. </div>
  73. )
  74. }
  75. export const LogoBox = ({ children, className }: { children: ReactNode; className?: string }) => (
  76. <div
  77. className={cn(
  78. 'flex size-12 items-center justify-center overflow-hidden rounded-xl border bg-muted',
  79. className
  80. )}
  81. >
  82. {children}
  83. </div>
  84. )
  85. export const LogoPair = ({ left, right }: { left: ReactNode; right: ReactNode }) => (
  86. <div className="flex items-center justify-center gap-2.5">
  87. {left}
  88. <ArrowRightLeft className="size-4 text-foreground-muted" />
  89. {right}
  90. </div>
  91. )
  92. export const BrivenLogo = () => (
  93. <LogoBox>
  94. <img alt="Briven" src={`${BASE_PATH}/img/briven-logo.svg`} className="size-7" />
  95. </LogoBox>
  96. )
  97. export const PartnerLogo = ({ src, alt }: { src: string; alt: string }) => (
  98. <LogoBox>
  99. <img alt={alt} src={src} className="size-full object-cover" />
  100. </LogoBox>
  101. )
  102. export const InterstitialAccountRow = ({
  103. avatarUrl,
  104. displayName,
  105. action,
  106. className,
  107. }: {
  108. avatarUrl?: string
  109. displayName?: string
  110. action?: ReactNode
  111. className?: string
  112. }) => (
  113. <Card className={cn('shadow-none', !action && 'border-muted bg-surface-200/50', className)}>
  114. <CardContent
  115. className={cn(
  116. 'flex gap-3 border-none',
  117. action ? 'items-center px-4 py-3' : 'items-start p-3'
  118. )}
  119. >
  120. <ProfileImage
  121. src={avatarUrl}
  122. alt={displayName}
  123. className="size-8 flex-shrink-0 rounded-full border border-muted"
  124. />
  125. <div className="min-w-0 flex-1">
  126. <p className="text-xs text-foreground-light">Signed in as</p>
  127. <p className="truncate text-sm text-foreground">
  128. {displayName || <span className="invisible">Loading account</span>}
  129. </p>
  130. </div>
  131. {action}
  132. </CardContent>
  133. </Card>
  134. )