Skip to content
Closed
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 @@ -49,6 +49,7 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([

'socialButtonsRoot',
'socialButtons',
'socialButtonsColumn',
'socialButtonsIconButton',
'socialButtonsBlockButton',
'socialButtonsBlockButtonText',
Expand Down
80 changes: 78 additions & 2 deletions packages/clerk-js/src/ui/elements/SocialButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, { forwardRef, isValidElement } from 'react';
import { ProviderInitialIcon } from '../common';
import type { LocalizationKey } from '../customizables';
import {
Box,
Button,
descriptors,
Flex,
Expand All @@ -18,10 +19,10 @@ import {
useAppearance,
} from '../customizables';
import { useEnabledThirdPartyProviders, useResizeObserver } from '../hooks';
import { mqu, type PropsOfComponent } from '../styledSystem';
import { mq, mqu, type PropsOfComponent } from '../styledSystem';
import { sleep } from '../utils';
import { useCardState } from './contexts';
import { distributeStrategiesIntoRows } from './utils';
import { distributeStrategiesIntoRows, getColumnCount } from './utils';

const SOCIAL_BUTTON_BLOCK_THRESHOLD = 2;
const SOCIAL_BUTTON_PRE_TEXT_THRESHOLD = 1;
Expand Down Expand Up @@ -57,6 +58,7 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => {
return null;
}

const strategyColumns = getColumnCount(strategies.length, MAX_STRATEGIES_PER_ROW);
const strategyRows = distributeStrategiesIntoRows([...strategies], MAX_STRATEGIES_PER_ROW);

const preferBlockButtons =
Expand Down Expand Up @@ -90,6 +92,80 @@ export const SocialButtons = React.memo((props: SocialButtonsRootProps) => {
gap={2}
elementDescriptor={descriptors.socialButtonsRoot}
>
<Flex
elementDescriptor={descriptors.socialButtons}
wrap='wrap'
justify='center'
sx={t => ({
rowGap: t.sizes.$2,
[mq.sm]: {
marginLeft: `calc(${t.sizes.$2}*-1)`,
},
})}
>
{strategies.map(strategy => {
const label =
strategies.length === SOCIAL_BUTTON_PRE_TEXT_THRESHOLD
? `Continue with ${strategyToDisplayData[strategy].name}`
: strategyToDisplayData[strategy].name;

const localizedText =
strategies.length === SOCIAL_BUTTON_PRE_TEXT_THRESHOLD
? localizationKeys('socialButtonsBlockButton', {
provider: strategyToDisplayData[strategy].name,
})
: localizationKeys('socialButtonsBlockButtonManyInView', {
provider: strategyToDisplayData[strategy].name,
});
const ref = null;

const imageOrInitial = strategyToDisplayData[strategy].iconUrl ? (
<Image
elementDescriptor={[descriptors.providerIcon, descriptors.socialButtonsProviderIcon]}
elementId={descriptors.socialButtonsProviderIcon.setId(strategyToDisplayData[strategy].id)}
isLoading={card.loadingMetadata === strategy}
isDisabled={card.isLoading}
src={strategyToDisplayData[strategy].iconUrl}
alt={`Sign in with ${strategyToDisplayData[strategy].name}`}
sx={theme => ({ width: theme.sizes.$4, height: theme.sizes.$4, maxWidth: '100%' })}
/>
) : (
<ProviderInitialIcon
id={strategyToDisplayData[strategy].id}
value={strategyToDisplayData[strategy].name}
isLoading={card.loadingMetadata === strategy}
isDisabled={card.isLoading}
/>
);

return (
<Box
key={strategy}
elementDescriptor={descriptors.socialButtonsColumn}
elementId={descriptors.socialButtonsProviderIcon.setId(strategyToDisplayData[strategy].id)}
sx={t => ({
[mq.sm]: {
paddingLeft: t.sizes.$2,
width: `calc(100%/${strategyColumns})`,
},
width: '100%',
})}
>
<ButtonElement
id={strategyToDisplayData[strategy].id}
ref={ref}
onClick={startOauth(strategy)}
isLoading={card.loadingMetadata === strategy}
isDisabled={card.isLoading}
label={label}
textLocalizationKey={localizedText}
icon={imageOrInitial}
/>
</Box>
);
})}
</Flex>
<Box sx={t => ({ height: t.sizes.$4 })} />
{strategyRows.map((row, rowIndex) => (
<Grid
key={row.join('-')}
Expand Down
47 changes: 47 additions & 0 deletions packages/clerk-js/src/ui/elements/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,50 @@ export function distributeStrategiesIntoRows<T>(strategies: T[], maxStrategiesPe

return rows;
}

/**
* Calculates the number of columns given the total number of items and the maximum columns allowed per row.
*
* @param {Object} options
* @param {number} options.length - The total number of items.
* @param {number} options.max - The maximum number of columns allowed per row.
* @returns The calculated number of columns.
*
* Example output for item counts from 1 to 24 with `columns: 6`:
*
* 1: [ 1 ]
* 2: [ 1, 2 ]
* 3: [ 1, 2, 3 ]
* 4: [ 1, 2, 3, 4 ]
* 5: [ 1, 2, 3, 4, 5 ]
* 6: [ 1, 2, 3, 4, 5, 6 ]
* 7: [ [1, 2, 3, 4], [5, 6, 7] ]
* 8: [ [1, 2, 3, 4], [5, 6, 7, 8] ]
* 9: [ [1, 2, 3, 4, 5], [6, 7, 8, 9] ]
* 10: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10] ]
* 11: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11] ]
* 12: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12] ]
* 13: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13] ]
* 14: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14] ]
* 15: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11], [12, 13, 14, 15] ]
* 16: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16] ]
* 17: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17] ]
* 18: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18] ]
* 19: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19] ]
* 20: [ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20] ]
* 21: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11], [12, 13, 14, 15, 16], [17, 18, 19, 20, 21] ]
* 22: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11], [12, 13, 14, 15, 16], [17, 18, 19, 20, 21, 22] ]
* 23: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17], [18, 19, 20, 21, 22, 23] ]
* 24: [ [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18], [19, 20, 21, 22, 23, 24] ]
*
* Examples:
* ```
* getColumnCount(1); // 1
* getColumnCount(7); // 4
* getColumnCount(15); // 6
* ```
*/
export function getColumnCount(length: number, max: number): number {
const numRows = Math.ceil(length / max);
return Math.ceil(length / numRows);
}
6 changes: 3 additions & 3 deletions packages/clerk-js/src/ui/styledSystem/breakpoints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const deviceQueries = {
ios: '@supports (-webkit-touch-callout: none)',
} as const;

// export const mq = Object.fromEntries(
// Object.entries(breakpoints).map(([k, v]) => [k, `@media (min-width: ${v})`]),
// ) as Record<keyof typeof breakpoints, string>;
export const mq = Object.fromEntries(
Object.entries(breakpoints).map(([k, v]) => [k, `@media (min-width: ${v})`]),
) as Record<keyof typeof breakpoints, string>;

export const mqu = {
...deviceQueries,
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/appearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export type ElementsConfig = {

socialButtonsRoot: WithOptions;
socialButtons: WithOptions;
socialButtonsColumn: WithOptions;
socialButtonsIconButton: WithOptions<OAuthProvider | Web3Provider, LoadingState>;
socialButtonsBlockButton: WithOptions<OAuthProvider | Web3Provider, LoadingState>;
socialButtonsBlockButtonText: WithOptions<OAuthProvider | Web3Provider>;
Expand Down
Loading