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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-
import type { Appearance as StripeAppearance, SetupIntent } from '@stripe/stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import type { PropsWithChildren } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import useSWR from 'swr';
import useSWRMutation from 'swr/mutation';

Expand All @@ -14,12 +14,12 @@ import { Form } from '@/ui/elements/Form';
import { FormButtons } from '@/ui/elements/FormButtons';
import { FormContainer } from '@/ui/elements/FormContainer';
import { handleError } from '@/ui/utils/errorHandler';
import { normalizeColorString } from '@/ui/utils/normalizeColorString';

import { clerkUnsupportedEnvironmentWarning } from '../../../core/errors';
import { useEnvironment, useSubscriberTypeContext, useSubscriberTypeLocalizationRoot } from '../../contexts';
import { descriptors, Flex, localizationKeys, Spinner, useAppearance, useLocalizations } from '../../customizables';
import type { LocalizationKey } from '../../localization';
import { resolveComputedCSSColor, resolveComputedCSSProperty } from '../../utils/cssVariables';

type AddPaymentSourceProps = {
onSuccess: (context: { stripeSetupIntent?: SetupIntent }) => Promise<void>;
Expand Down Expand Up @@ -84,30 +84,72 @@ const AddPaymentSourceRoot = ({ children, ...rest }: PropsWithChildren<AddPaymen
const [headerSubtitle, setHeaderSubtitle] = useState<LocalizationKey | undefined>(undefined);
const [submitLabel, setSubmitLabel] = useState<LocalizationKey | undefined>(undefined);

const { colors, fontWeights, fontSizes, radii, space } = useAppearance().parsedInternalTheme;
const [elementsAppearance, setElementsAppearance] = useState<StripeAppearance>({});

useEffect(() => {
void initializePaymentSource();
}, []);
}, [initializePaymentSource]);

return (
<AddPaymentSourceContext.Provider
value={{
value: {
headerTitle,
headerSubtitle,
submitLabel,
setHeaderTitle,
setHeaderSubtitle,
setSubmitLabel,
initializePaymentSource,
externalClientSecret,
stripe,
paymentMethodOrder,
...rest,
const themeRefCallback = useCallback(
(node: HTMLDivElement | null) => {
if (!node) {
return;
}

const appearance: StripeAppearance = {
variables: {
colorPrimary: resolveComputedCSSColor(node, colors.$primary500, colors.$colorBackground),
colorBackground: resolveComputedCSSColor(node, colors.$colorInputBackground, colors.$colorBackground),
colorText: resolveComputedCSSColor(node, colors.$colorText, colors.$colorBackground),
colorTextSecondary: resolveComputedCSSColor(node, colors.$colorTextSecondary, colors.$colorBackground),
colorSuccess: resolveComputedCSSColor(node, colors.$success500, colors.$colorBackground),
colorDanger: resolveComputedCSSColor(node, colors.$danger500, colors.$colorBackground),
colorWarning: resolveComputedCSSColor(node, colors.$warning500, colors.$colorBackground),
fontWeightNormal: resolveComputedCSSProperty(node, 'font-weight', fontWeights.$normal.toString()),
fontWeightMedium: resolveComputedCSSProperty(node, 'font-weight', fontWeights.$medium.toString()),
fontWeightBold: resolveComputedCSSProperty(node, 'font-weight', fontWeights.$bold.toString()),
fontSizeXl: resolveComputedCSSProperty(node, 'font-size', fontSizes.$xl),
fontSizeLg: resolveComputedCSSProperty(node, 'font-size', fontSizes.$lg),
fontSizeSm: resolveComputedCSSProperty(node, 'font-size', fontSizes.$md),
fontSizeXs: resolveComputedCSSProperty(node, 'font-size', fontSizes.$sm),
borderRadius: resolveComputedCSSProperty(node, 'border-radius', radii.$lg),
spacingUnit: resolveComputedCSSProperty(node, 'padding', space.$1),
},
}}
>
{children}
</AddPaymentSourceContext.Provider>
};

setElementsAppearance(appearance);
},
[colors, fontWeights, fontSizes, radii, space],
);

return (
<>
<div
style={{ display: 'none' }}
ref={themeRefCallback}
/>
<AddPaymentSourceContext.Provider
value={{
value: {
headerTitle,
headerSubtitle,
submitLabel,
setHeaderTitle,
setHeaderSubtitle,
setSubmitLabel,
initializePaymentSource,
externalClientSecret,
stripe,
paymentMethodOrder,
elementsAppearance,
...rest,
},
}}
>
{children}
</AddPaymentSourceContext.Provider>
</>
);
};

Expand All @@ -122,29 +164,7 @@ const AddPaymentSourceLoading = (props: PropsWithChildren) => {
};

const AddPaymentSourceReady = (props: PropsWithChildren) => {
const { externalClientSecret, stripe } = useAddPaymentSourceContext();

const { colors, fontWeights, fontSizes, radii, space } = useAppearance().parsedInternalTheme;
const elementsAppearance: StripeAppearance = {
variables: {
colorPrimary: normalizeColorString(colors.$primary500),
colorBackground: normalizeColorString(colors.$colorInputBackground),
colorText: normalizeColorString(colors.$colorText),
colorTextSecondary: normalizeColorString(colors.$colorTextSecondary),
colorSuccess: normalizeColorString(colors.$success500),
colorDanger: normalizeColorString(colors.$danger500),
colorWarning: normalizeColorString(colors.$warning500),
fontWeightNormal: fontWeights.$normal.toString(),
fontWeightMedium: fontWeights.$medium.toString(),
fontWeightBold: fontWeights.$bold.toString(),
fontSizeXl: fontSizes.$xl,
fontSizeLg: fontSizes.$lg,
fontSizeSm: fontSizes.$md,
fontSizeXs: fontSizes.$sm,
borderRadius: radii.$md,
spacingUnit: space.$1,
},
};
const { externalClientSecret, stripe, elementsAppearance } = useAddPaymentSourceContext();

if (!stripe || !externalClientSecret) {
return null;
Expand Down
25 changes: 8 additions & 17 deletions packages/clerk-js/src/ui/customizables/parseVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,11 @@ export const createRadiiUnits = (theme: Theme) => {
}

const md = borderRadius === 'none' ? '0' : borderRadius;
const { numericValue, unit = 'rem' } = splitCssUnit(md);
return {
sm: (numericValue * 0.66).toString() + unit,
sm: `calc(${md} * 0.66)`,
md,
lg: (numericValue * 1.33).toString() + unit,
xl: (numericValue * 2).toString() + unit,
lg: `calc(${md} * 1.33)`,
xl: `calc(${md} * 2)`,
};
};

Expand All @@ -64,12 +63,11 @@ export const createSpaceScale = (theme: Theme) => {
if (spacingUnit === undefined) {
return;
}
const { numericValue, unit } = splitCssUnit(spacingUnit);
return fromEntries(
spaceScaleKeys.map(k => {
const num = Number.parseFloat(k.replace('x', '.'));
const percentage = (num / 0.5) * 0.125;
return [k, `${numericValue * percentage}${unit}`];
return [k, `calc(${spacingUnit} * ${percentage})`];
}),
);
};
Expand All @@ -82,13 +80,12 @@ export const createFontSizeScale = (theme: Theme): Record<keyof typeof fontSizes
if (fontSize === undefined) {
return;
}
const { numericValue, unit = 'rem' } = splitCssUnit(fontSize);
return {
xs: (numericValue * 0.8).toString() + unit,
sm: (numericValue * 0.9).toString() + unit,
xs: `calc(${fontSize} * 0.8)`,
sm: `calc(${fontSize} * 0.9)`,
md: fontSize,
lg: (numericValue * 1.3).toString() + unit,
xl: (numericValue * 1.85).toString() + unit,
lg: `calc(${fontSize} * 1.3)`,
xl: `calc(${fontSize} * 1.85)`,
};
};

Expand All @@ -101,9 +98,3 @@ export const createFonts = (theme: Theme) => {
const { fontFamily, fontFamilyButtons } = theme.variables || {};
return removeUndefinedProps({ main: fontFamily, buttons: fontFamilyButtons });
};

const splitCssUnit = (str: string) => {
const numericValue = Number.parseFloat(str);
const unit = str.replace(numericValue.toString(), '') || undefined;
return { numericValue, unit };
};
Loading