diff --git a/app/dashboard/src/components/AriaComponents/Dialog/Dialog.tsx b/app/dashboard/src/components/AriaComponents/Dialog/Dialog.tsx index 6b76a961adbf2..b03965a3ef143 100644 --- a/app/dashboard/src/components/AriaComponents/Dialog/Dialog.tsx +++ b/app/dashboard/src/components/AriaComponents/Dialog/Dialog.tsx @@ -9,13 +9,14 @@ import * as portal from '#/components/Portal' import * as suspense from '#/components/Suspense' import * as mergeRefs from '#/utilities/mergeRefs' -import * as twv from '#/utilities/tailwindVariants' +import type { VariantProps } from '#/utilities/tailwindVariants' +import { tv } from '#/utilities/tailwindVariants' import * as dialogProvider from './DialogProvider' import * as dialogStackProvider from './DialogStackProvider' import type * as types from './types' import * as utlities from './utilities' -import * as variants from './variants' +import { DIALOG_BACKGROUND } from './variants' // ================= // === Constants === @@ -25,10 +26,10 @@ import * as variants from './variants' */ export interface DialogProps extends types.DialogProps, - Omit, 'scrolledToTop'> {} + Omit, 'scrolledToTop'> {} -const OVERLAY_STYLES = twv.tv({ - base: 'fixed inset-0 isolate flex items-center justify-center bg-black/[25%]', +const OVERLAY_STYLES = tv({ + base: 'fixed inset-0 isolate flex items-center justify-center bg-primary/20', variants: { isEntering: { true: 'animate-in fade-in duration-200 ease-out' }, isExiting: { true: 'animate-out fade-out duration-200 ease-in' }, @@ -36,17 +37,23 @@ const OVERLAY_STYLES = twv.tv({ }, }) -const MODAL_STYLES = twv.tv({ - base: 'fixed inset-0 flex items-center justify-center text-center text-xs text-primary p-4', +const MODAL_STYLES = tv({ + base: 'fixed inset-0 flex items-center justify-center text-xs text-primary', variants: { - isEntering: { true: 'animate-in slide-in-from-top-1 ease-out duration-200' }, - isExiting: { true: 'animate-out slide-out-to-top-1 ease-in duration-200' }, + isEntering: { true: 'animate-in ease-out duration-200' }, + isExiting: { true: 'animate-out ease-in duration-200' }, + type: { modal: '', fullscreen: 'p-3.5' }, }, + compoundVariants: [ + { type: 'modal', isEntering: true, class: 'slide-in-from-top-1' }, + { type: 'modal', isExiting: true, class: 'slide-out-to-top-1' }, + { type: 'fullscreen', isEntering: true, class: 'zoom-in-[1.015]' }, + { type: 'fullscreen', isExiting: true, class: 'zoom-out-[1.015]' }, + ], }) -const DIALOG_STYLES = twv.tv({ - extend: variants.DIALOG_STYLES, - base: 'w-full overflow-y-auto', +const DIALOG_STYLES = tv({ + base: DIALOG_BACKGROUND({ className: 'w-full flex flex-col text-left align-middle shadow-xl' }), variants: { type: { modal: { @@ -74,6 +81,16 @@ const DIALOG_STYLES = twv.tv({ content: 'isolate', }, }, + rounded: { + none: { base: '' }, + small: { base: 'rounded-sm' }, + medium: { base: 'rounded-md' }, + large: { base: 'rounded-lg' }, + xlarge: { base: 'rounded-xl' }, + xxlarge: { base: 'rounded-2xl', content: 'scroll-offset-edge-2xl' }, + xxxlarge: { base: 'rounded-3xl', content: 'scroll-offset-edge-4xl' }, + xxxxlarge: { base: 'rounded-4xl', content: 'scroll-offset-edge-6xl' }, + }, /** * The size of the dialog. * Only applies to the `modal` type. @@ -120,6 +137,7 @@ const DIALOG_STYLES = twv.tv({ hideCloseButton: false, size: 'medium', padding: 'medium', + rounded: 'xxlarge', }, }) @@ -146,6 +164,7 @@ export function Dialog(props: DialogProps) { rounded, padding = type === 'modal' ? 'medium' : 'xlarge', fitContent, + variants = DIALOG_STYLES, ...ariaDialogProps } = props @@ -155,11 +174,13 @@ export function Dialog(props: DialogProps) { * Handles the scroll event on the dialog content. */ const handleScroll = (scrollTop: number) => { - if (scrollTop > 0) { - setIsScrolledToTop(false) - } else { - setIsScrolledToTop(true) - } + React.startTransition(() => { + if (scrollTop > 0) { + setIsScrolledToTop(false) + } else { + setIsScrolledToTop(true) + } + }) } const dialogId = aria.useId() @@ -169,7 +190,7 @@ export function Dialog(props: DialogProps) { const overlayState = React.useRef(null) const root = portal.useStrictPortalContext() - const styles = DIALOG_STYLES({ + const styles = variants({ className, type, rounded, @@ -200,11 +221,7 @@ export function Dialog(props: DialogProps) { return ( - OVERLAY_STYLES({ - isEntering, - isExiting, - blockInteractions: !isDismissable, - }) + OVERLAY_STYLES({ isEntering, isExiting, blockInteractions: !isDismissable }) } isDismissable={isDismissable} isKeyboardDismissDisabled={isKeyboardDismissDisabled} @@ -218,7 +235,7 @@ export function Dialog(props: DialogProps) { return ( MODAL_STYLES({ type, isEntering, isExiting })} isDismissable={isDismissable} isKeyboardDismissDisabled={isKeyboardDismissDisabled} UNSTABLE_portalContainer={root} @@ -252,25 +269,23 @@ export function Dialog(props: DialogProps) { {(opts) => { return ( - {((!hideCloseButton && closeButton !== 'floating') || title != null) && ( - - + + - {title != null && ( - - {title} - - )} - - )} + {title != null && ( + + {title} + + )} +
{ diff --git a/app/dashboard/src/components/AriaComponents/Dialog/variants.ts b/app/dashboard/src/components/AriaComponents/Dialog/variants.ts index b803e20e67874..c8f7ea6bb3bef 100644 --- a/app/dashboard/src/components/AriaComponents/Dialog/variants.ts +++ b/app/dashboard/src/components/AriaComponents/Dialog/variants.ts @@ -14,19 +14,4 @@ export const DIALOG_BACKGROUND = twv.tv({ export const DIALOG_STYLES = twv.tv({ extend: DIALOG_BACKGROUND, base: 'flex flex-col text-left align-middle shadow-xl', - variants: { - rounded: { - none: '', - small: 'rounded-sm', - medium: 'rounded-md', - large: 'rounded-lg', - xlarge: 'rounded-xl', - xxlarge: 'rounded-2xl', - xxxlarge: 'rounded-3xl', - xxxxlarge: 'rounded-4xl', - }, - }, - defaultVariants: { - rounded: 'xxlarge', - }, }) diff --git a/app/dashboard/src/modules/payments/components/PlanSelector/PlanSelector.tsx b/app/dashboard/src/modules/payments/components/PlanSelector/PlanSelector.tsx index 8a11b5f79861b..4837df1151a4e 100644 --- a/app/dashboard/src/modules/payments/components/PlanSelector/PlanSelector.tsx +++ b/app/dashboard/src/modules/payments/components/PlanSelector/PlanSelector.tsx @@ -7,6 +7,8 @@ import { useAuth } from '#/providers/AuthProvider' import { useRemoteBackendStrict } from '#/providers/BackendProvider' import { useText } from '#/providers/TextProvider' import { Plan, PLANS } from '#/services/Backend' +import type { VariantProps } from '#/utilities/tailwindVariants' +import { tv } from '#/utilities/tailwindVariants' import { Card } from './components' import { getComponentPerPlan } from './getComponentForPlan' @@ -26,7 +28,7 @@ interface CreateCheckoutSessionMutation { /** * Props for {@link PlanSelector} */ -export interface PlanSelectorProps { +export interface PlanSelectorProps extends VariantProps { readonly showFreePlan?: boolean readonly hasTrial?: boolean readonly userPlan?: Plan | undefined @@ -36,6 +38,26 @@ export interface PlanSelectorProps { readonly onSubscribeError?: (error: Error) => void } +const PLAN_SELECTOR_STYLES = tv({ + base: DIALOG_BACKGROUND({ + className: 'w-full snap-x overflow-auto rounded-4xl scroll-hidden', + }), + variants: { + showFreePlan: { + true: { + grid: 'grid-cols-1fr md:grid-cols-2 xl:grid-cols-4', + }, + false: { + grid: 'grid-cols-1fr md:grid-cols-3 justify-center', + }, + }, + }, + slots: { + grid: 'inline-grid min-w-full gap-6 p-6', + card: 'min-w-64 snap-center', + }, +}) + /** * Plan selector component. * Shows the available plans and allows the user to subscribe to one. @@ -49,6 +71,7 @@ export function PlanSelector(props: PlanSelectorProps) { showFreePlan = true, hasTrial = true, isOrganizationAdmin = false, + variants = PLAN_SELECTOR_STYLES, } = props const { getText } = useText() @@ -77,13 +100,11 @@ export function PlanSelector(props: PlanSelectorProps) { onError: (error) => onSubscribeError?.(error), }) + const classes = variants({ showFreePlan }) + return ( -
-
+
+
{PLANS.map((newPlan) => { const paywallLevel = getPaywallLevel(newPlan) const userPaywallLevel = getPaywallLevel(userPlan) @@ -96,7 +117,7 @@ export function PlanSelector(props: PlanSelectorProps) { return ( { - if (username === defaultName) { + // If user is already created we shouldn't call `setUsername` if value wasn't changed + if (username === defaultName && isUserCreated) { goToNextStep() return } else {