mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Improve auth method selection (#442)
* move brand icons * add icons * modify the auth page * change how dialog works * preview * improve the auth methods page * better predicate types * convert to table * fix more stuff * refactor * add default case * edit table * add config * add brand colors * icon refresh * fix a bug with shared tooltips * Refactor auth methods page and preview with UI improvements * Simplify provider update confirmation with async/await * Update packages/stack-ui/src/components/brand-icons.tsx Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com> * update deps * more fixes --------- Co-authored-by: Zai Shi <zaishi00@outlook.com> Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
This commit is contained in:
parent
018b04e825
commit
6692194250
@ -124,23 +124,20 @@ export default function PageClient () {
|
||||
<Separator orientation="vertical" />
|
||||
|
||||
<div className="w-1/2 self-stretch py-4 px-4 lg:px-20 bg-zinc-300 dark:bg-zinc-800 hidden md:flex items-center">
|
||||
{
|
||||
(
|
||||
<div className="w-full">
|
||||
<BrowserFrame url="your-website.com/signin">
|
||||
<div className="flex flex-col items-center justify-center min-h-[400px]">
|
||||
<div className='w-full sm:max-w-xs m-auto scale-90 pointer-events-none' inert=''>
|
||||
{/* a transparent cover that prevents the card from being clicked, even when pointer-events is overridden */}
|
||||
<div className="absolute inset-0 bg-transparent z-10"></div>
|
||||
<AuthPage
|
||||
type="sign-in"
|
||||
mockProject={mockProject}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BrowserFrame>
|
||||
<div className="w-full">
|
||||
<BrowserFrame url="your-website.com/signin">
|
||||
<div className="flex flex-col items-center justify-center min-h-[400px]">
|
||||
<div className='w-full sm:max-w-xs m-auto scale-90 pointer-events-none' inert=''>
|
||||
{/* a transparent cover that prevents the card from being clicked, even when pointer-events is overridden */}
|
||||
<div className="absolute inset-0 bg-transparent z-10"></div>
|
||||
<AuthPage
|
||||
type="sign-in"
|
||||
mockProject={mockProject}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</BrowserFrame>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import { SettingCard, SettingSwitch } from "@/components/settings";
|
||||
import { AdminOAuthProviderConfig, AuthPage, OAuthProviderConfig } from "@stackframe/stack";
|
||||
import { allProviders } from "@stackframe/stack-shared/dist/utils/oauth";
|
||||
import { ActionDialog, Typography } from "@stackframe/stack-ui";
|
||||
import { ActionDialog, Badge, BrandIcons, BrowserFrame, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Input, SimpleTooltip, Typography } from "@stackframe/stack-ui";
|
||||
import { AsteriskSquare, CirclePlus, Key, Link2, MoreHorizontal } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { CardSubtitle } from "../../../../../../../../../packages/stack-ui/dist/components/ui/card";
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { useAdminApp } from "../use-admin-app";
|
||||
import { ProviderSettingSwitch } from "./providers";
|
||||
import { ProviderSettingDialog, ProviderSettingSwitch, TurnOffProviderDialog } from "./providers";
|
||||
|
||||
function ConfirmSignUpEnabledDialog(props: {
|
||||
open?: boolean,
|
||||
@ -73,57 +75,33 @@ function ConfirmSignUpDisabledDialog(props: {
|
||||
);
|
||||
}
|
||||
|
||||
export default function PageClient() {
|
||||
function DisabledProvidersDialog({ open, onOpenChange }: { open?: boolean, onOpenChange?: (open: boolean) => void }) {
|
||||
const stackAdminApp = useAdminApp();
|
||||
const project = stackAdminApp.useProject();
|
||||
const oauthProviders = project.config.oauthProviders;
|
||||
const [confirmSignUpEnabled, setConfirmSignUpEnabled] = useState(false);
|
||||
const [confirmSignUpDisabled, setConfirmSignUpDisabled] = useState(false);
|
||||
const [providerSearch, setProviderSearch] = useState("");
|
||||
const filteredProviders = allProviders
|
||||
.filter((id) => id.toLowerCase().includes(providerSearch.toLowerCase()))
|
||||
.map((id) => [id, oauthProviders.find((provider) => provider.id === id)] as const)
|
||||
.filter(([, provider]) => {
|
||||
return !provider?.enabled;
|
||||
});
|
||||
|
||||
return (
|
||||
<PageLayout title="Auth Methods" description="Configure how users can sign in to your app">
|
||||
<SettingCard>
|
||||
<CardSubtitle>
|
||||
Email-based
|
||||
</CardSubtitle>
|
||||
<SettingSwitch
|
||||
label="Email password authentication"
|
||||
checked={project.config.credentialEnabled}
|
||||
onCheckedChange={async (checked) => {
|
||||
await project.update({
|
||||
config: {
|
||||
credentialEnabled: checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SettingSwitch
|
||||
label="Magic link/OTP"
|
||||
checked={project.config.magicLinkEnabled}
|
||||
onCheckedChange={async (checked) => {
|
||||
await project.update({
|
||||
config: {
|
||||
magicLinkEnabled: checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SettingSwitch
|
||||
label="Passkey"
|
||||
checked={project.config.passkeyEnabled}
|
||||
onCheckedChange={async (checked) => {
|
||||
await project.update({
|
||||
config: {
|
||||
passkeyEnabled: checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CardSubtitle className="mt-2">
|
||||
SSO (OAuth)
|
||||
</CardSubtitle>
|
||||
{allProviders.map((id) => {
|
||||
const provider = oauthProviders.find((provider) => provider.id === id);
|
||||
return <ActionDialog
|
||||
title="Add New Auth Method"
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
cancelButton
|
||||
>
|
||||
<Input
|
||||
className="mb-4"
|
||||
placeholder="Search for a provider..."
|
||||
value={providerSearch}
|
||||
onChange={(e) => setProviderSearch(e.target.value)}
|
||||
/>
|
||||
<div className="flex gap-2 flex-wrap justify-center">
|
||||
{filteredProviders
|
||||
.map(([id, provider]) => {
|
||||
return <ProviderSettingSwitch
|
||||
key={id}
|
||||
id={id}
|
||||
@ -140,7 +118,207 @@ export default function PageClient() {
|
||||
}}
|
||||
/>;
|
||||
})}
|
||||
</SettingCard>
|
||||
|
||||
{ filteredProviders.length === 0 && <Typography variant="secondary">No providers found.</Typography> }
|
||||
</div>
|
||||
|
||||
</ActionDialog>;
|
||||
}
|
||||
|
||||
function OAuthActionCell({ config }: { config: AdminOAuthProviderConfig }) {
|
||||
const stackAdminApp = useAdminApp();
|
||||
const project = stackAdminApp.useProject();
|
||||
const oauthProviders = project.config.oauthProviders;
|
||||
const [turnOffProviderDialogOpen, setTurnOffProviderDialogOpen] = useState(false);
|
||||
const [providerSettingDialogOpen, setProviderSettingDialogOpen] = useState(false);
|
||||
|
||||
|
||||
const updateProvider = async (provider: AdminOAuthProviderConfig & OAuthProviderConfig) => {
|
||||
const alreadyExist = oauthProviders.some((p) => p.id === config.id);
|
||||
const newOAuthProviders = oauthProviders.map((p) => p.id === config.id ? provider : p);
|
||||
if (!alreadyExist) {
|
||||
newOAuthProviders.push(provider);
|
||||
}
|
||||
await project.update({
|
||||
config: { oauthProviders: newOAuthProviders },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<TurnOffProviderDialog
|
||||
open={turnOffProviderDialogOpen}
|
||||
onClose={() => setTurnOffProviderDialogOpen(false)}
|
||||
providerId={config.id}
|
||||
onConfirm={async () => {
|
||||
await updateProvider({
|
||||
...config,
|
||||
id: config.id,
|
||||
enabled: false
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ProviderSettingDialog
|
||||
id={config.id}
|
||||
open={providerSettingDialogOpen}
|
||||
onClose={() => setProviderSettingDialogOpen(false)}
|
||||
updateProvider={updateProvider}
|
||||
/>
|
||||
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => { setProviderSettingDialogOpen(true); }}>
|
||||
Configure
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="text-red-400"
|
||||
onClick={() => { setTurnOffProviderDialogOpen(true); }}
|
||||
>
|
||||
Disable Provider
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
const SHARED_TOOLTIP = "Shared keys are automatically created by Stack, but show Stack's logo on the OAuth sign-in page.\n\nYou should replace these before you go into production.";
|
||||
|
||||
export default function PageClient() {
|
||||
const stackAdminApp = useAdminApp();
|
||||
const project = stackAdminApp.useProject();
|
||||
const oauthProviders = project.config.oauthProviders;
|
||||
const [confirmSignUpEnabled, setConfirmSignUpEnabled] = useState(false);
|
||||
const [confirmSignUpDisabled, setConfirmSignUpDisabled] = useState(false);
|
||||
const [disabledProvidersDialogOpen, setDisabledProvidersDialogOpen] = useState(false);
|
||||
|
||||
const enabledProviders = allProviders
|
||||
.map((id) => [id, oauthProviders.find((provider) => provider.id === id)] as const)
|
||||
.filter(([, provider]) => provider?.enabled);
|
||||
|
||||
return (
|
||||
<PageLayout title="Auth Methods" description="Configure how users can sign in to your app">
|
||||
<div className="flex gap-4">
|
||||
<SettingCard className="flex-grow">
|
||||
<SettingSwitch
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
<AsteriskSquare size={20} aria-hidden="true" />
|
||||
<span>Email/password authentication</span>
|
||||
</div>
|
||||
}
|
||||
checked={project.config.credentialEnabled}
|
||||
onCheckedChange={async (checked) => {
|
||||
await project.update({
|
||||
config: {
|
||||
credentialEnabled: checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SettingSwitch
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
<Link2 size={20} />
|
||||
<span>Magic link (Email OTP)</span>
|
||||
</div>
|
||||
}
|
||||
checked={project.config.magicLinkEnabled}
|
||||
onCheckedChange={async (checked) => {
|
||||
await project.update({
|
||||
config: {
|
||||
magicLinkEnabled: checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SettingSwitch
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
<Key size={20} />
|
||||
<span>Passkey</span>
|
||||
</div>
|
||||
}
|
||||
checked={project.config.passkeyEnabled}
|
||||
onCheckedChange={async (checked) => {
|
||||
await project.update({
|
||||
config: {
|
||||
passkeyEnabled: checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CardSubtitle className="mt-2">
|
||||
SSO Providers
|
||||
</CardSubtitle>
|
||||
|
||||
{ enabledProviders.map(([, provider]) => provider)
|
||||
.filter((provider): provider is AdminOAuthProviderConfig => !!provider).map(provider => {
|
||||
return <div key={provider.id} className="flex h-10 items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="flex items-center justify-center w-12 h-12 rounded-md border border-gray-800"
|
||||
style={{ backgroundColor: BrandIcons.BRAND_COLORS[provider.id] ?? undefined }}
|
||||
>
|
||||
<BrandIcons.Mapping iconSize={24} provider={provider.id} />
|
||||
</div>
|
||||
<span className="text-sm font-semibold">{BrandIcons.toTitle(provider.id)}</span>
|
||||
{provider.type === 'shared' && <SimpleTooltip tooltip={SHARED_TOOLTIP}>
|
||||
<Badge variant="secondary">Shared keys</Badge>
|
||||
</SimpleTooltip>}
|
||||
</div>
|
||||
|
||||
<OAuthActionCell config={provider} />
|
||||
</div>;
|
||||
}) }
|
||||
|
||||
<Button
|
||||
className="mt-4"
|
||||
onClick={() => {
|
||||
setDisabledProvidersDialogOpen(true);
|
||||
}}
|
||||
variant="secondary"
|
||||
>
|
||||
<CirclePlus size={16}/>
|
||||
<span className="ml-2">Add SSO providers</span>
|
||||
</Button>
|
||||
<DisabledProvidersDialog
|
||||
open={disabledProvidersDialogOpen}
|
||||
onOpenChange={(x) => {
|
||||
setDisabledProvidersDialogOpen(x);
|
||||
}}
|
||||
/>
|
||||
</SettingCard>
|
||||
<SettingCard className="hidden lg:flex">
|
||||
<div className="self-stretch py-4 px-4 min-w-[400px] items-center">
|
||||
<div className="w-full">
|
||||
<BrowserFrame url="your-website.com/signin">
|
||||
<div className="flex flex-col items-center justify-center min-h-[400px]">
|
||||
<div className='w-full sm:max-w-xs m-auto scale-90 pointer-events-none' inert=''>
|
||||
{/* a transparent cover that prevents the card from being clicked, even when pointer-events is overridden */}
|
||||
<div className="absolute inset-0 bg-transparent z-10"></div>
|
||||
<AuthPage
|
||||
type="sign-in"
|
||||
mockProject={{
|
||||
config: {
|
||||
...project.config,
|
||||
oauthProviders: enabledProviders
|
||||
.map(([, provider]) => provider)
|
||||
.filter((provider): provider is AdminOAuthProviderConfig => !!provider),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BrowserFrame>
|
||||
</div>
|
||||
</div>
|
||||
</SettingCard>
|
||||
</div>
|
||||
<SettingCard title="Sign-up">
|
||||
<SettingSwitch
|
||||
label="Allow new user sign-ups"
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
"use client";
|
||||
import { FormDialog } from "@/components/form-dialog";
|
||||
import { InputField, SwitchField } from "@/components/form-fields";
|
||||
import { SettingIconButton, SettingSwitch } from "@/components/settings";
|
||||
import { getPublicEnvVar } from '@stackframe/stack-shared/dist/utils/env';
|
||||
import { AdminProject } from "@stackframe/stack";
|
||||
import type { AdminProject } from "@stackframe/stack";
|
||||
import { yupBoolean, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { getPublicEnvVar } from '@stackframe/stack-shared/dist/utils/env';
|
||||
import { sharedProviders } from "@stackframe/stack-shared/dist/utils/oauth";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { ActionDialog, Badge, InlineCode, Label, SimpleTooltip, Typography } from "@stackframe/stack-ui";
|
||||
import { ActionDialog, Badge, BrandIcons, InlineCode, Label, SimpleTooltip, Typography } from "@stackframe/stack-ui";
|
||||
import clsx from "clsx";
|
||||
import { useState } from "react";
|
||||
import * as yup from "yup";
|
||||
|
||||
@ -160,7 +159,7 @@ export function ProviderSettingDialog(props: Props & { open: boolean, onClose: (
|
||||
export function TurnOffProviderDialog(props: {
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
onConfirm: () => void,
|
||||
onConfirm: () => Promise<void>,
|
||||
providerId: string,
|
||||
}) {
|
||||
return (
|
||||
@ -172,7 +171,7 @@ export function TurnOffProviderDialog(props: {
|
||||
okButton={{
|
||||
label: `Disable ${toTitle(props.providerId)}`,
|
||||
onClick: async () => {
|
||||
props.onConfirm();
|
||||
await props.onConfirm();
|
||||
},
|
||||
}}
|
||||
cancelButton
|
||||
@ -202,35 +201,33 @@ export function ProviderSettingSwitch(props: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingSwitch
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
{toTitle(props.id)}
|
||||
{isShared && enabled &&
|
||||
<SimpleTooltip tooltip={"Shared keys are automatically created by Stack, but show Stack's logo on the OAuth sign-in page.\n\nYou should replace these before you go into production."}>
|
||||
<Badge variant="secondary">Shared keys</Badge>
|
||||
</SimpleTooltip>
|
||||
}
|
||||
</div>
|
||||
<div className={clsx("flex flex-col items-center justify-center gap-2 py-2 border border-1 rounded-lg p-2 w-[120px] h-[120px] cursor-pointer transition-all",
|
||||
enabled ? "border-white" : "border-gray-800 hover:border-gray-400"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (enabled) {
|
||||
setTurnOffProviderDialogOpen(true);
|
||||
} else {
|
||||
setProviderSettingDialogOpen(true);
|
||||
}
|
||||
checked={enabled}
|
||||
onCheckedChange={async (checked) => {
|
||||
if (!checked) {
|
||||
setTurnOffProviderDialogOpen(true);
|
||||
return;
|
||||
} else {
|
||||
setProviderSettingDialogOpen(true);
|
||||
}
|
||||
}}
|
||||
actions={<SettingIconButton onClick={() => setProviderSettingDialogOpen(true)} />}
|
||||
onlyShowActionsWhenChecked
|
||||
/>
|
||||
}}
|
||||
>
|
||||
<BrandIcons.Mapping iconSize={28} provider={props.id} />
|
||||
<span className="text-sm">{toTitle(props.id)}</span>
|
||||
{isShared && enabled &&
|
||||
<SimpleTooltip tooltip={"Shared keys are automatically created by Stack, but show Stack's logo on the OAuth sign-in page.\n\nYou should replace these before you go into production."}>
|
||||
<Badge variant="secondary">Shared keys</Badge>
|
||||
</SimpleTooltip>
|
||||
}
|
||||
</div>
|
||||
|
||||
<TurnOffProviderDialog
|
||||
open={TurnOffProviderDialogOpen}
|
||||
onClose={() => setTurnOffProviderDialogOpen(false)}
|
||||
providerId={props.id}
|
||||
onConfirm={() => runAsynchronously(updateProvider(false))}
|
||||
onConfirm={async () => {
|
||||
await updateProvider(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<ProviderSettingDialog {...props} open={ProviderSettingDialogOpen} onClose={() => setProviderSettingDialogOpen(false)} />
|
||||
|
||||
@ -173,7 +173,7 @@ export const getCommonUserColumns = <T extends ExtendedServerUser>() => [
|
||||
},
|
||||
] satisfies ColumnDef<T>[];
|
||||
|
||||
const columns: ColumnDef<ExtendedServerUser>[] = [
|
||||
const columns: ColumnDef<ExtendedServerUser>[] = [
|
||||
...getCommonUserColumns<ExtendedServerUser>(),
|
||||
{
|
||||
accessorKey: "authTypes",
|
||||
|
||||
@ -14,9 +14,10 @@ export function SettingCard(props: {
|
||||
actions?: React.ReactNode,
|
||||
children?: React.ReactNode,
|
||||
accordion?: string,
|
||||
className?: string,
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<Card className={props.className}>
|
||||
{(props.title || props.description) && (
|
||||
<CardHeader>
|
||||
{props.title && <CardTitle>{props.title}</CardTitle>}
|
||||
|
||||
217
packages/stack-ui/src/components/brand-icons.tsx
Normal file
217
packages/stack-ui/src/components/brand-icons.tsx
Normal file
@ -0,0 +1,217 @@
|
||||
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
|
||||
export function Google({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 24 24">
|
||||
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
|
||||
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
|
||||
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
|
||||
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
|
||||
<path fill="none" d="M1 1h22v22H1z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Facebook({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 512 512">
|
||||
<path fill='#FFFFFF' d="M512 256C512 114.6 397.4 0 256 0S0 114.6 0 256C0 376 82.7 476.8 194.2 504.5V334.2H141.4V256h52.8V222.3c0-87.1 39.4-127.5 125-127.5c16.2 0 44.2 3.2 55.7 6.4V172c-6-.6-16.5-1-29.6-1c-42 0-58.2 15.9-58.2 57.2V256h83.6l-14.4 78.2H287V510.1C413.8 494.8 512 386.9 512 256h0z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function GitHub({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 496 512">
|
||||
<path fill='#FFFFFF' d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Microsoft({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 21 21">
|
||||
<title>{"MS-SymbolLockup"}</title>
|
||||
<path fill="#f25022" d="M1 1h9v9H1z" />
|
||||
<path fill="#00a4ef" d="M1 11h9v9H1z" />
|
||||
<path fill="#7fba00" d="M11 1h9v9h-9z" />
|
||||
<path fill="#ffb900" d="M11 11h9v9h-9z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Spotify({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 496 512">
|
||||
<path fill='#ffffff' d="M248 8C111.1 8 0 119.1 0 256s111.1 248 248 248 248-111.1 248-248S384.9 8 248 8zm100.7 364.9c-4.2 0-6.8-1.3-10.7-3.6-62.4-37.6-135-39.2-206.7-24.5-3.9 1-9 2.6-11.9 2.6-9.7 0-15.8-7.7-15.8-15.8 0-10.3 6.1-15.2 13.6-16.8 81.9-18.1 165.6-16.5 237 26.2 6.1 3.9 9.7 7.4 9.7 16.5s-7.1 15.4-15.2 15.4zm26.9-65.6c-5.2 0-8.7-2.3-12.3-4.2-62.5-37-155.7-51.9-238.6-29.4-4.8 1.3-7.4 2.6-11.9 2.6-10.7 0-19.4-8.7-19.4-19.4s5.2-17.8 15.5-20.7c27.8-7.8 56.2-13.6 97.8-13.6 64.9 0 127.6 16.1 177 45.5 8.1 4.8 11.3 11 11.3 19.7-.1 10.8-8.5 19.5-19.4 19.5zm31-76.2c-5.2 0-8.4-1.3-12.9-3.9-71.2-42.5-198.5-52.7-280.9-29.7-3.6 1-8.1 2.6-12.9 2.6-13.2 0-23.3-10.3-23.3-23.6 0-13.6 8.4-21.3 17.4-23.9 35.2-10.3 74.6-15.2 117.5-15.2 73 0 149.5 15.2 205.4 47.8 7.8 4.5 12.9 10.7 12.9 22.6 0 13.6-11 23.3-23.2 23.3z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Discord({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 127.14 96.36">
|
||||
<path fill="#fff" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Gitlab({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
viewBox="0 -10 256 256"
|
||||
version="1.1"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
>
|
||||
<g>
|
||||
<path d="M128.07485,236.074667 L128.07485,236.074667 L175.17885,91.1043048 L80.9708495,91.1043048 L128.07485,236.074667 L128.07485,236.074667 Z" fill="#E24329"></path>
|
||||
<path d="M128.07485,236.074423 L80.9708495,91.104061 L14.9557638,91.104061 L128.07485,236.074423 L128.07485,236.074423 Z" fill="#FC6D26"></path>
|
||||
<path d="M14.9558857,91.1044267 L14.9558857,91.1044267 L0.641828571,135.159589 C-0.663771429,139.17757 0.766171429,143.57955 4.18438095,146.06275 L128.074971,236.074789 L14.9558857,91.1044267 L14.9558857,91.1044267 Z" fill="#FCA326"></path>
|
||||
<path d="M14.9558857,91.1045486 L80.9709714,91.1045486 L52.6000762,3.79026286 C51.1408762,-0.703146667 44.7847619,-0.701927619 43.3255619,3.79026286 L14.9558857,91.1045486 L14.9558857,91.1045486 Z" fill="#E24329"></path>
|
||||
<path d="M128.07485,236.074423 L175.17885,91.104061 L241.193935,91.104061 L128.07485,236.074423 L128.07485,236.074423 Z" fill="#FC6D26"></path>
|
||||
<path d="M241.193935,91.1044267 L241.193935,91.1044267 L255.507992,135.159589 C256.813592,139.17757 255.38365,143.57955 251.96544,146.06275 L128.07485,236.074789 L241.193935,91.1044267 L241.193935,91.1044267 Z" fill="#FCA326"></path>
|
||||
<path d="M241.193935,91.1045486 L175.17885,91.1045486 L203.549745,3.79026286 C205.008945,-0.703146667 211.365059,-0.701927619 212.824259,3.79026286 L241.193935,91.1045486 L241.193935,91.1045486 Z" fill="#E24329"></path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Bitbucket({ iconSize }: { iconSize: number }) {
|
||||
return (
|
||||
<svg
|
||||
preserveAspectRatio="xMidYMid"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="-0.9662264221278978 -0.5824607696358868 257.93281329857973 230.8324730411935"
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
>
|
||||
<linearGradient
|
||||
id="a"
|
||||
x1="108.633%"
|
||||
x2="46.927%"
|
||||
y1="13.818%"
|
||||
y2="78.776%"
|
||||
>
|
||||
<stop offset=".18" stopColor="#0052cc" />
|
||||
<stop offset="1" stopColor="#2684ff" />
|
||||
</linearGradient>
|
||||
<g fill="none">
|
||||
<path d="M101.272 152.561h53.449l12.901-75.32H87.06z" />
|
||||
<path d="M8.308 0A8.202 8.202 0 0 0 .106 9.516l34.819 211.373a11.155 11.155 0 0 0 10.909 9.31h167.04a8.202 8.202 0 0 0 8.201-6.89l34.82-213.752a8.202 8.202 0 0 0-8.203-9.514zm146.616 152.768h-53.315l-14.436-75.42h80.67z" fill="#2684ff"/>
|
||||
<path d="M244.61 77.242h-76.916l-12.909 75.36h-53.272l-62.902 74.663a11.105 11.105 0 0 0 7.171 2.704H212.73a8.196 8.196 0 0 0 8.196-6.884z" fill="url(#a)"/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function LinkedIn({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#fff"
|
||||
height={iconSize}
|
||||
width={iconSize}
|
||||
viewBox="0 0 310 310"
|
||||
>
|
||||
<g id="XMLID_801_">
|
||||
<path id="XMLID_802_" d="M72.16,99.73H9.927c-2.762,0-5,2.239-5,5v199.928c0,2.762,2.238,5,5,5H72.16c2.762,0,5-2.238,5-5V104.73 C77.16,101.969,74.922,99.73,72.16,99.73z" />
|
||||
<path id="XMLID_803_" d="M41.066,0.341C18.422,0.341,0,18.743,0,41.362C0,63.991,18.422,82.4,41.066,82.4 c22.626,0,41.033-18.41,41.033-41.038C82.1,18.743,63.692,0.341,41.066,0.341z" />
|
||||
<path id="XMLID_804_" d="M230.454,94.761c-24.995,0-43.472,10.745-54.679,22.954V104.73c0-2.761-2.238-5-5-5h-59.599 c-2.762,0-5,2.239-5,5v199.928c0,2.762,2.238,5,5,5h62.097c2.762,0,5-2.238,5-5v-98.918c0-33.333,9.054-46.319,32.29-46.319 c25.306,0,27.317,20.818,27.317,48.034v97.204c0,2.762,2.238,5,5,5H305c2.762,0,5-2.238,5-5V194.995 C310,145.43,300.549,94.761,230.454,94.761z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Apple({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg fill="#fff" height={iconSize} width={iconSize} version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.773 22.773">
|
||||
<path d="M15.769,0c0.053,0,0.106,0,0.162,0c0.13,1.606-0.483,2.806-1.228,3.675c-0.731,0.863-1.732,1.7-3.351,1.573c-0.108-1.583,0.506-2.694,1.25-3.561C13.292,0.879,14.557,0.16,15.769,0z"/>
|
||||
<path d="M20.67,16.716c0,0.016,0,0.03,0,0.045c-0.455,1.378-1.104,2.559-1.896,3.655c-0.723,0.995-1.609,2.334-3.191,2.334c-1.367,0-2.275-0.879-3.676-0.903c-1.482-0.024-2.297,0.735-3.652,0.926c-0.155,0-0.31,0-0.462,0c-0.995-0.144-1.798-0.932-2.383-1.642c-1.725-2.098-3.058-4.808-3.306-8.276c0-0.34,0-0.679,0-1.019c0.105-2.482,1.311-4.5,2.914-5.478c0.846-0.52,2.009-0.963,3.304-0.765c0.555,0.086,1.122,0.276,1.619,0.464c0.471,0.181,1.06,0.502,1.618,0.485c0.378-0.011,0.754-0.208,1.135-0.347c1.116-0.403,2.21-0.865,3.652-0.648c1.733,0.262,2.963,1.032,3.723,2.22c-1.466,0.933-2.625,2.339-2.427,4.74C17.818,14.688,19.086,15.964,20.67,16.716z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function X({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg aria-label="X" viewBox="0 0 1200 1227" width={iconSize} height={iconSize} xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#FFFFFF" d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Mapping({
|
||||
provider,
|
||||
iconSize,
|
||||
}: {
|
||||
provider: string,
|
||||
iconSize: number,
|
||||
}) {
|
||||
switch (provider) {
|
||||
case "google": {
|
||||
return <Google iconSize={iconSize} />;
|
||||
}
|
||||
case "github": {
|
||||
return <GitHub iconSize={iconSize} />;
|
||||
}
|
||||
case "facebook": {
|
||||
return <Facebook iconSize={iconSize} />;
|
||||
}
|
||||
case "microsoft": {
|
||||
return <Microsoft iconSize={iconSize} />;
|
||||
}
|
||||
case "spotify": {
|
||||
return <Spotify iconSize={iconSize} />;
|
||||
}
|
||||
case "discord": {
|
||||
return <Discord iconSize={iconSize} />;
|
||||
}
|
||||
case "gitlab": {
|
||||
return <Gitlab iconSize={iconSize} />;
|
||||
}
|
||||
case "bitbucket": {
|
||||
return <Bitbucket iconSize={iconSize} />;
|
||||
}
|
||||
case "linkedin": {
|
||||
return <LinkedIn iconSize={iconSize} />;
|
||||
}
|
||||
case "apple": {
|
||||
return <Apple iconSize={iconSize} />;
|
||||
}
|
||||
case "x": {
|
||||
return <X iconSize={iconSize} />;
|
||||
}
|
||||
default: {
|
||||
throw new StackAssertionError(`Icon not found for provider: ${provider}`);;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function toTitle(id: string) {
|
||||
return {
|
||||
github: "GitHub",
|
||||
google: "Google",
|
||||
facebook: "Facebook",
|
||||
microsoft: "Microsoft",
|
||||
spotify: "Spotify",
|
||||
discord: "Discord",
|
||||
gitlab: "GitLab",
|
||||
apple: "Apple",
|
||||
bitbucket: "Bitbucket",
|
||||
linkedin: "LinkedIn",
|
||||
x: "X",
|
||||
}[id] || throwErr(`Unknown provider: ${id}`);
|
||||
}
|
||||
|
||||
export const BRAND_COLORS: Record<string, string> = {
|
||||
github: '#24292e',
|
||||
google: '#ffffff',
|
||||
facebook: '#0866FF',
|
||||
microsoft: '#2F2F2F',
|
||||
spotify: '#1DD65F',
|
||||
discord: '#5661F5',
|
||||
linkedin: '#0A66C2',
|
||||
x: '#000000',
|
||||
};
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
export * from "./components/action-dialog";
|
||||
export * as BrandIcons from "./components/brand-icons";
|
||||
export * from "./components/browser-frame";
|
||||
export * from "./components/copy-button";
|
||||
export * from "./components/copy-field";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@stackframe/stack-ui';
|
||||
import { BrandIcons, Button } from '@stackframe/stack-ui';
|
||||
import Color from 'color';
|
||||
import { useId } from 'react';
|
||||
import { useStackApp } from '..';
|
||||
@ -8,146 +8,6 @@ import { useTranslation } from '../lib/translations';
|
||||
|
||||
const iconSize = 22;
|
||||
|
||||
function GoogleIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 24 24">
|
||||
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
|
||||
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
|
||||
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
|
||||
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
|
||||
<path fill="none" d="M1 1h22v22H1z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function FacebookIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 512 512">
|
||||
<path fill='#FFFFFF' d="M512 256C512 114.6 397.4 0 256 0S0 114.6 0 256C0 376 82.7 476.8 194.2 504.5V334.2H141.4V256h52.8V222.3c0-87.1 39.4-127.5 125-127.5c16.2 0 44.2 3.2 55.7 6.4V172c-6-.6-16.5-1-29.6-1c-42 0-58.2 15.9-58.2 57.2V256h83.6l-14.4 78.2H287V510.1C413.8 494.8 512 386.9 512 256h0z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function GitHubIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 496 512">
|
||||
<path fill='#FFFFFF' d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function MicrosoftIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 21 21">
|
||||
<title>{"MS-SymbolLockup"}</title>
|
||||
<path fill="#f25022" d="M1 1h9v9H1z" />
|
||||
<path fill="#00a4ef" d="M1 11h9v9H1z" />
|
||||
<path fill="#7fba00" d="M11 1h9v9h-9z" />
|
||||
<path fill="#ffb900" d="M11 11h9v9h-9z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function SpotifyIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 496 512">
|
||||
<path fill='#ffffff' d="M248 8C111.1 8 0 119.1 0 256s111.1 248 248 248 248-111.1 248-248S384.9 8 248 8zm100.7 364.9c-4.2 0-6.8-1.3-10.7-3.6-62.4-37.6-135-39.2-206.7-24.5-3.9 1-9 2.6-11.9 2.6-9.7 0-15.8-7.7-15.8-15.8 0-10.3 6.1-15.2 13.6-16.8 81.9-18.1 165.6-16.5 237 26.2 6.1 3.9 9.7 7.4 9.7 16.5s-7.1 15.4-15.2 15.4zm26.9-65.6c-5.2 0-8.7-2.3-12.3-4.2-62.5-37-155.7-51.9-238.6-29.4-4.8 1.3-7.4 2.6-11.9 2.6-10.7 0-19.4-8.7-19.4-19.4s5.2-17.8 15.5-20.7c27.8-7.8 56.2-13.6 97.8-13.6 64.9 0 127.6 16.1 177 45.5 8.1 4.8 11.3 11 11.3 19.7-.1 10.8-8.5 19.5-19.4 19.5zm31-76.2c-5.2 0-8.4-1.3-12.9-3.9-71.2-42.5-198.5-52.7-280.9-29.7-3.6 1-8.1 2.6-12.9 2.6-13.2 0-23.3-10.3-23.3-23.6 0-13.6 8.4-21.3 17.4-23.9 35.2-10.3 74.6-15.2 117.5-15.2 73 0 149.5 15.2 205.4 47.8 7.8 4.5 12.9 10.7 12.9 22.6 0 13.6-11 23.3-23.2 23.3z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
function DiscordIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={iconSize} height={iconSize} viewBox="0 0 127.14 96.36">
|
||||
<path fill="#fff" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
function GitlabIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
viewBox="0 -10 256 256"
|
||||
version="1.1"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
>
|
||||
<g>
|
||||
<path d="M128.07485,236.074667 L128.07485,236.074667 L175.17885,91.1043048 L80.9708495,91.1043048 L128.07485,236.074667 L128.07485,236.074667 Z" fill="#E24329"></path>
|
||||
<path d="M128.07485,236.074423 L80.9708495,91.104061 L14.9557638,91.104061 L128.07485,236.074423 L128.07485,236.074423 Z" fill="#FC6D26"></path>
|
||||
<path d="M14.9558857,91.1044267 L14.9558857,91.1044267 L0.641828571,135.159589 C-0.663771429,139.17757 0.766171429,143.57955 4.18438095,146.06275 L128.074971,236.074789 L14.9558857,91.1044267 L14.9558857,91.1044267 Z" fill="#FCA326"></path>
|
||||
<path d="M14.9558857,91.1045486 L80.9709714,91.1045486 L52.6000762,3.79026286 C51.1408762,-0.703146667 44.7847619,-0.701927619 43.3255619,3.79026286 L14.9558857,91.1045486 L14.9558857,91.1045486 Z" fill="#E24329"></path>
|
||||
<path d="M128.07485,236.074423 L175.17885,91.104061 L241.193935,91.104061 L128.07485,236.074423 L128.07485,236.074423 Z" fill="#FC6D26"></path>
|
||||
<path d="M241.193935,91.1044267 L241.193935,91.1044267 L255.507992,135.159589 C256.813592,139.17757 255.38365,143.57955 251.96544,146.06275 L128.07485,236.074789 L241.193935,91.1044267 L241.193935,91.1044267 Z" fill="#FCA326"></path>
|
||||
<path d="M241.193935,91.1045486 L175.17885,91.1045486 L203.549745,3.79026286 C205.008945,-0.703146667 211.365059,-0.701927619 212.824259,3.79026286 L241.193935,91.1045486 L241.193935,91.1045486 Z" fill="#E24329"></path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function BitbucketIcon({ iconSize }: { iconSize: number }) {
|
||||
return (
|
||||
<svg
|
||||
preserveAspectRatio="xMidYMid"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="-0.9662264221278978 -0.5824607696358868 257.93281329857973 230.8324730411935"
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
>
|
||||
<linearGradient
|
||||
id="a"
|
||||
x1="108.633%"
|
||||
x2="46.927%"
|
||||
y1="13.818%"
|
||||
y2="78.776%"
|
||||
>
|
||||
<stop offset=".18" stopColor="#0052cc" />
|
||||
<stop offset="1" stopColor="#2684ff" />
|
||||
</linearGradient>
|
||||
<g fill="none">
|
||||
<path d="M101.272 152.561h53.449l12.901-75.32H87.06z" />
|
||||
<path d="M8.308 0A8.202 8.202 0 0 0 .106 9.516l34.819 211.373a11.155 11.155 0 0 0 10.909 9.31h167.04a8.202 8.202 0 0 0 8.201-6.89l34.82-213.752a8.202 8.202 0 0 0-8.203-9.514zm146.616 152.768h-53.315l-14.436-75.42h80.67z" fill="#2684ff"/>
|
||||
<path d="M244.61 77.242h-76.916l-12.909 75.36h-53.272l-62.902 74.663a11.105 11.105 0 0 0 7.171 2.704H212.73a8.196 8.196 0 0 0 8.196-6.884z" fill="url(#a)"/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function LinkedInIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#fff"
|
||||
height={iconSize}
|
||||
width={iconSize}
|
||||
viewBox="0 0 310 310"
|
||||
>
|
||||
<g id="XMLID_801_">
|
||||
<path id="XMLID_802_" d="M72.16,99.73H9.927c-2.762,0-5,2.239-5,5v199.928c0,2.762,2.238,5,5,5H72.16c2.762,0,5-2.238,5-5V104.73 C77.16,101.969,74.922,99.73,72.16,99.73z" />
|
||||
<path id="XMLID_803_" d="M41.066,0.341C18.422,0.341,0,18.743,0,41.362C0,63.991,18.422,82.4,41.066,82.4 c22.626,0,41.033-18.41,41.033-41.038C82.1,18.743,63.692,0.341,41.066,0.341z" />
|
||||
<path id="XMLID_804_" d="M230.454,94.761c-24.995,0-43.472,10.745-54.679,22.954V104.73c0-2.761-2.238-5-5-5h-59.599 c-2.762,0-5,2.239-5,5v199.928c0,2.762,2.238,5,5,5h62.097c2.762,0,5-2.238,5-5v-98.918c0-33.333,9.054-46.319,32.29-46.319 c25.306,0,27.317,20.818,27.317,48.034v97.204c0,2.762,2.238,5,5,5H305c2.762,0,5-2.238,5-5V194.995 C310,145.43,300.549,94.761,230.454,94.761z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function AppleIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg fill="#fff" height={iconSize} width={iconSize} version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.773 22.773">
|
||||
<path d="M15.769,0c0.053,0,0.106,0,0.162,0c0.13,1.606-0.483,2.806-1.228,3.675c-0.731,0.863-1.732,1.7-3.351,1.573c-0.108-1.583,0.506-2.694,1.25-3.561C13.292,0.879,14.557,0.16,15.769,0z"/>
|
||||
<path d="M20.67,16.716c0,0.016,0,0.03,0,0.045c-0.455,1.378-1.104,2.559-1.896,3.655c-0.723,0.995-1.609,2.334-3.191,2.334c-1.367,0-2.275-0.879-3.676-0.903c-1.482-0.024-2.297,0.735-3.652,0.926c-0.155,0-0.31,0-0.462,0c-0.995-0.144-1.798-0.932-2.383-1.642c-1.725-2.098-3.058-4.808-3.306-8.276c0-0.34,0-0.679,0-1.019c0.105-2.482,1.311-4.5,2.914-5.478c0.846-0.52,2.009-0.963,3.304-0.765c0.555,0.086,1.122,0.276,1.619,0.464c0.471,0.181,1.06,0.502,1.618,0.485c0.378-0.011,0.754-0.208,1.135-0.347c1.116-0.403,2.21-0.865,3.652-0.648c1.733,0.262,2.963,1.032,3.723,2.22c-1.466,0.933-2.625,2.339-2.427,4.74C17.818,14.688,19.086,15.964,20.67,16.716z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function XIcon({ iconSize } : { iconSize: number} ) {
|
||||
return (
|
||||
<svg aria-label="X" viewBox="0 0 1200 1227" width={iconSize} height={iconSize} xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#FFFFFF" d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const changeColor = (c: Color, value: number) => {
|
||||
if (c.isLight()) {
|
||||
value = -value;
|
||||
@ -180,7 +40,7 @@ export function OAuthButton({
|
||||
textColor: '#000',
|
||||
name: 'Google',
|
||||
border: '1px solid #ddd',
|
||||
icon: <GoogleIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Google iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -190,7 +50,7 @@ export function OAuthButton({
|
||||
textColor: '#fff',
|
||||
border: '1px solid #333',
|
||||
name: 'GitHub',
|
||||
icon: <GitHubIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.GitHub iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -199,7 +59,7 @@ export function OAuthButton({
|
||||
backgroundColor: '#1877F2',
|
||||
textColor: '#fff',
|
||||
name: 'Facebook',
|
||||
icon: <FacebookIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Facebook iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -208,7 +68,7 @@ export function OAuthButton({
|
||||
backgroundColor: '#2f2f2f',
|
||||
textColor: '#fff',
|
||||
name: 'Microsoft',
|
||||
icon: <MicrosoftIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Microsoft iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -217,7 +77,7 @@ export function OAuthButton({
|
||||
backgroundColor: '#1DB954',
|
||||
textColor: '#fff',
|
||||
name: 'Spotify',
|
||||
icon: <SpotifyIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Spotify iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -226,7 +86,7 @@ export function OAuthButton({
|
||||
backgroundColor: '#5865F2',
|
||||
textColor: '#fff',
|
||||
name: 'Discord',
|
||||
icon: <DiscordIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Discord iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -236,7 +96,7 @@ export function OAuthButton({
|
||||
textColor: "#fff",
|
||||
border: "1px solid #333",
|
||||
name: "Gitlab",
|
||||
icon: <GitlabIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Gitlab iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -246,7 +106,7 @@ export function OAuthButton({
|
||||
textColor: "#fff",
|
||||
border: "1px solid #333",
|
||||
name: "Apple",
|
||||
icon: <AppleIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Apple iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -256,7 +116,7 @@ export function OAuthButton({
|
||||
textColor: "#000",
|
||||
border: "1px solid #ddd",
|
||||
name: "Bitbucket",
|
||||
icon: <BitbucketIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.Bitbucket iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -265,7 +125,7 @@ export function OAuthButton({
|
||||
backgroundColor: "#0073b1",
|
||||
textColor: "#fff",
|
||||
name: "LinkedIn",
|
||||
icon: <LinkedInIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.LinkedIn iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@ -274,7 +134,7 @@ export function OAuthButton({
|
||||
backgroundColor: "#000",
|
||||
textColor: "#fff",
|
||||
name: "X",
|
||||
icon: <XIcon iconSize={iconSize} />,
|
||||
icon: <BrandIcons.X iconSize={iconSize} />,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user