diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/icons/bulled-list.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/icons/bulled-list.tsx new file mode 100644 index 0000000000..edd3db03e9 --- /dev/null +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/icons/bulled-list.tsx @@ -0,0 +1,19 @@ +export function BulledListIcon({ color = 'currentColor' }: { color: string }) { + return ( + + + + ); +} diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx index 0c1e7af33b..1fe7c6ef95 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx @@ -9,6 +9,7 @@ import { updateSubscriptionMutation, } from '@affine/graphql'; import { useMutation, useQuery } from '@affine/workspace/affine/gql'; +import { DoneIcon } from '@blocksuite/icons'; import { Button } from '@toeverything/components/button'; import { type PropsWithChildren, @@ -24,6 +25,7 @@ import { type SubscriptionMutator, useUserSubscription, } from '../../../../../hooks/use-subscription'; +import { BulledListIcon } from './icons/bulled-list'; import * as styles from './style.css'; interface FixedPrice { @@ -108,6 +110,7 @@ const planDetail = new Map([ const Settings = () => { const [subscription, mutateSubscription] = useUserSubscription(); const loggedIn = useCurrentLoginStatus() === 'authenticated'; + const scrollWrapper = useRef(null); const { data: { prices }, @@ -139,6 +142,32 @@ const Settings = () => { planDetail.get(SubscriptionPlan.Pro) as FixedPrice | undefined )?.discount; + // auto scroll to current plan card + useEffect(() => { + if (!scrollWrapper.current) return; + const currentPlanCard = scrollWrapper.current?.querySelector( + '[data-current="true"]' + ); + const wrapperComputedStyle = getComputedStyle(scrollWrapper.current); + const left = currentPlanCard + ? currentPlanCard.getBoundingClientRect().left - + scrollWrapper.current.getBoundingClientRect().left - + parseInt(wrapperComputedStyle.paddingLeft) + : 0; + const appeared = + scrollWrapper.current.getAttribute('data-appeared') === 'true'; + const animationFrameId = requestAnimationFrame(() => { + scrollWrapper.current?.scrollTo({ + behavior: appeared ? 'smooth' : 'instant', + left, + }); + scrollWrapper.current?.setAttribute('data-appeared', 'true'); + }); + return () => { + cancelAnimationFrame(animationFrameId); + }; + }, [recurring]); + return ( <> { ))} - {/* TODO: plan cards horizontal scroll behavior is not the same as design */} - {/* TODO: may scroll current plan into view when first loading? */} -
+
{Array.from(planDetail.values()).map(detail => { + const isCurrent = + loggedIn && + detail.plan === currentPlan && + (currentPlan === SubscriptionPlan.Free + ? true + : currentRecurring === recurring); return (

@@ -198,23 +228,27 @@ const Settings = () => { )}

-

- {detail.type === 'dynamic' ? ( - - Coming soon... - - ) : ( - <> - - $ - {recurring === SubscriptionRecurring.Monthly - ? detail.price - : detail.yearlyPrice} +

+

+ {detail.type === 'dynamic' ? ( + + Coming soon... - per month - - )} -

+ ) : ( + <> + + $ + {recurring === SubscriptionRecurring.Monthly + ? detail.price + : detail.yearlyPrice} + + + per month + + + )} +

+
{ // branches: // if contact => 'Contact Sales' @@ -264,8 +298,11 @@ const Settings = () => { {detail.benefits.map((content, i) => (
- {/* TODO: icons */} - {detail.type == 'dynamic' ? '·' : '✅'} + {detail.type == 'dynamic' ? ( + + ) : ( + + )}
{content}
diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/style.css.ts b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/style.css.ts index 636169e78e..c3587350d2 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/style.css.ts +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/style.css.ts @@ -22,6 +22,8 @@ export const planCardsWrapper = style({ display: 'flex', overflowX: 'auto', scrollSnapType: 'x mandatory', + // TODO: should display the horizontal scrollbar, ensure the box-shadow is not clipped + paddingBottom: '21px', }); export const planCard = style({ @@ -75,6 +77,13 @@ export const planTitle = style({ fontWeight: 600, }); +export const planPriceWrapper = style({ + minHeight: '28px', + lineHeight: 1, + display: 'flex', + alignItems: 'flex-end', +}); + export const planPrice = style({ fontSize: 'var(--affine-font-h-5)', marginRight: '8px',