@workos-inc/widgets 1.1.5 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/cjs/lib/organization-switcher.d.ts +10 -1
  2. package/dist/cjs/lib/organization-switcher.d.ts.map +1 -1
  3. package/dist/cjs/lib/organization-switcher.js +31 -3
  4. package/dist/cjs/lib/organization-switcher.js.map +1 -1
  5. package/dist/cjs/workos-widgets.client.d.ts +6 -0
  6. package/dist/cjs/workos-widgets.client.d.ts.map +1 -1
  7. package/dist/cjs/workos-widgets.client.js +23 -3
  8. package/dist/cjs/workos-widgets.client.js.map +1 -1
  9. package/dist/esm/lib/organization-switcher.d.ts +10 -1
  10. package/dist/esm/lib/organization-switcher.d.ts.map +1 -1
  11. package/dist/esm/lib/organization-switcher.js +31 -3
  12. package/dist/esm/lib/organization-switcher.js.map +1 -1
  13. package/dist/esm/workos-widgets.client.d.ts +6 -0
  14. package/dist/esm/workos-widgets.client.d.ts.map +1 -1
  15. package/dist/esm/workos-widgets.client.js +25 -5
  16. package/dist/esm/workos-widgets.client.js.map +1 -1
  17. package/package.json +8 -9
  18. package/src/api/api-provider.tsx +0 -158
  19. package/src/api/constants.ts +0 -1
  20. package/src/api/endpoint.ts +0 -3097
  21. package/src/api/errors.ts +0 -48
  22. package/src/api/index.ts +0 -2
  23. package/src/api/utils.ts +0 -42
  24. package/src/api/widgets-api-client.ts +0 -87
  25. package/src/card-list.tsx +0 -26
  26. package/src/index.ts +0 -9
  27. package/src/lib/add-mfa-dialog.tsx +0 -379
  28. package/src/lib/api/config.ts +0 -9
  29. package/src/lib/api/user.ts +0 -98
  30. package/src/lib/change-password-dialog.tsx +0 -290
  31. package/src/lib/constants.ts +0 -3
  32. package/src/lib/copy-button.tsx +0 -53
  33. package/src/lib/delete-user-dialog.tsx +0 -110
  34. package/src/lib/edit-user-profile-dialog.tsx +0 -181
  35. package/src/lib/edit-user-role-dialog.tsx +0 -178
  36. package/src/lib/elements.tsx +0 -428
  37. package/src/lib/elevated-access.tsx +0 -261
  38. package/src/lib/error-boundary.tsx +0 -166
  39. package/src/lib/errors.ts +0 -49
  40. package/src/lib/generic-error.tsx +0 -70
  41. package/src/lib/icon-panel.tsx +0 -26
  42. package/src/lib/icons.tsx +0 -21
  43. package/src/lib/invite-user-dialog.tsx +0 -327
  44. package/src/lib/logout-all-sessions-dialog.tsx +0 -82
  45. package/src/lib/logout-dialog.tsx +0 -85
  46. package/src/lib/marker.tsx +0 -39
  47. package/src/lib/oauth-icons.tsx +0 -138
  48. package/src/lib/organization-switcher.tsx +0 -156
  49. package/src/lib/otp-input.tsx +0 -276
  50. package/src/lib/resend-invite-dialog.tsx +0 -145
  51. package/src/lib/reset-mfa-dialog.tsx +0 -104
  52. package/src/lib/revoke-invite-dialog.tsx +0 -111
  53. package/src/lib/save-button.tsx +0 -113
  54. package/src/lib/search-provider.tsx +0 -51
  55. package/src/lib/set-password-dialog.tsx +0 -204
  56. package/src/lib/use-dialog-close.tsx +0 -19
  57. package/src/lib/use-is-hydrated.ts +0 -13
  58. package/src/lib/use-layout-effect.ts +0 -6
  59. package/src/lib/use-security-settings.tsx +0 -49
  60. package/src/lib/user-actions-dropdown.tsx +0 -157
  61. package/src/lib/user-profile.tsx +0 -227
  62. package/src/lib/user-security.tsx +0 -187
  63. package/src/lib/user-sessions.tsx +0 -204
  64. package/src/lib/users-filter.tsx +0 -62
  65. package/src/lib/users-management-context.tsx +0 -74
  66. package/src/lib/users-management-state.ts +0 -165
  67. package/src/lib/users-management.tsx +0 -594
  68. package/src/lib/users-search.tsx +0 -73
  69. package/src/lib/utils.ts +0 -131
  70. package/src/lib/widgets-context.ts +0 -29
  71. package/src/organization-switcher.client.tsx +0 -81
  72. package/src/user-profile.client.tsx +0 -55
  73. package/src/user-security.client.tsx +0 -55
  74. package/src/user-sessions.client.tsx +0 -100
  75. package/src/users-management.client.tsx +0 -73
  76. package/src/workos-widgets.client.tsx +0 -75
  77. /package/{src → dist/css}/base.css +0 -0
  78. /package/{src → dist/css}/lib/card-list.css +0 -0
  79. /package/{src → dist/css}/lib/marker.css +0 -0
  80. /package/{src → dist/css}/lib/save-button.css +0 -0
  81. /package/{src → dist/css}/styles.css +0 -0
  82. /package/{src → dist/css}/users-management.css +0 -0
@@ -1,327 +0,0 @@
1
- "use client";
2
-
3
- import {
4
- Callout,
5
- Dialog,
6
- Flex,
7
- Select,
8
- Text,
9
- VisuallyHidden,
10
- } from "@radix-ui/themes";
11
- import * as React from "react";
12
- import {
13
- DialogContent,
14
- PrimaryButton,
15
- SecondaryButton,
16
- SelectContent,
17
- SelectItem,
18
- SelectTrigger,
19
- TextField,
20
- } from "./elements";
21
- import { Label } from "./elements";
22
- import { isErrorLike } from "./utils";
23
- import { useInviteUser } from "./api/user";
24
- import { InviteMemberInput, MemberRole, useRoles } from "../api";
25
-
26
- /**
27
- * Used to stub a fake value for the role select. It will be selected by default
28
- * before the role query resolves, or if the query results in an error. We do
29
- * this because we need to provide _any_ value to the select to avoid
30
- * controlled/uncontrolled bugs.
31
- */
32
- const PLACEHOLDER_ROLE = "_rolePlaceholder";
33
-
34
- interface InviteUserDialogProps {
35
- children?: React.ReactNode;
36
- }
37
-
38
- export function InviteUserDialog({ children }: InviteUserDialogProps) {
39
- const [open, setOpen] = React.useState(false);
40
- const dialogId = toId("invite-user", React.useId());
41
- const formId = toId(dialogId, "form");
42
-
43
- const inviteUser = useInviteUser();
44
- const rolesQuery = useRoles({
45
- query: { initialData: [] },
46
- });
47
- const roles = rolesQuery.data;
48
- const [selectedRole, setSelectedRole] = React.useState(
49
- () => getDefaultRole(roles)?.slug || PLACEHOLDER_ROLE,
50
- );
51
- React.useEffect(() => {
52
- // Update the selected role if it's not in the list (eg if the list was
53
- // previously empty and the query resolved)
54
- setSelectedRole((selectedRole) => {
55
- if (roles.find((role) => role.slug === selectedRole)) {
56
- // if current selected role is in the new list, don't change it
57
- return selectedRole;
58
- }
59
- return getDefaultRole(roles)?.slug || PLACEHOLDER_ROLE;
60
- });
61
- }, [roles]);
62
-
63
- const onSubmitForm = (data: InviteMemberInput) => {
64
- if (inviteUser.isPending || rolesQuery.status !== "success") {
65
- return;
66
- }
67
- inviteUser.mutate(
68
- { data },
69
- {
70
- onSuccess: () => {
71
- setOpen(false);
72
- },
73
- },
74
- );
75
- };
76
-
77
- const formErrors = getFormErrors(inviteUser.error);
78
- useFormFieldFocusOnError(dialogId, inviteUser.error);
79
-
80
- return (
81
- <Dialog.Root open={open} onOpenChange={setOpen}>
82
- {children && <Dialog.Trigger>{children}</Dialog.Trigger>}
83
- <DialogContent maxWidth="480px" key={String(open)}>
84
- <Dialog.Title>Invite user</Dialog.Title>
85
- <Dialog.Description>
86
- An invitation will be sent to this email address with a link to
87
- complete their account.
88
- </Dialog.Description>
89
- <Flex direction="column" gap="4" mt="5" asChild>
90
- <form
91
- id={formId}
92
- onSubmit={async (event) => {
93
- event.preventDefault();
94
- onSubmitForm({
95
- email: event.currentTarget.email.value,
96
- roles: [selectedRole],
97
- });
98
- }}
99
- >
100
- <FormField
101
- rootId={dialogId}
102
- name="email"
103
- label="Email address"
104
- error={formErrors.fields.email}
105
- required
106
- control={(props) => (
107
- <TextField
108
- {...props}
109
- data-1p-ignore="true"
110
- data-lpignore="true"
111
- type="email"
112
- autoComplete="off"
113
- placeholder="Enter an email address"
114
- />
115
- )}
116
- />
117
-
118
- <FormField
119
- rootId={dialogId}
120
- name="role"
121
- label="Role"
122
- error={formErrors.fields.role}
123
- disabled={rolesQuery.isPending || roles.length <= 1}
124
- info={
125
- roles.length === 1 ? (
126
- <>
127
- New users will be invited with the{" "}
128
- <Text weight="bold">{roles[0].name}</Text> role, as it is
129
- the only one available.
130
- </>
131
- ) : undefined
132
- }
133
- control={({
134
- id,
135
- "aria-invalid": ariaInvalid,
136
- "aria-describedby": ariaDescribedBy,
137
- ...props
138
- }) => (
139
- <Select.Root
140
- {...props}
141
- value={selectedRole}
142
- onValueChange={setSelectedRole}
143
- >
144
- <SelectTrigger
145
- id={id}
146
- aria-invalid={ariaInvalid}
147
- aria-describedby={ariaDescribedBy}
148
- />
149
- <SelectContent>
150
- <SelectItem value={PLACEHOLDER_ROLE} disabled>
151
- Select a role
152
- </SelectItem>
153
- {roles.map((role) => (
154
- <SelectItem key={role.slug} value={role.slug}>
155
- {role.name}
156
- </SelectItem>
157
- ))}
158
- </SelectContent>
159
- </Select.Root>
160
- )}
161
- />
162
- </form>
163
- </Flex>
164
-
165
- {formErrors.form ? (
166
- <Callout.Root color="red" mt="4" mb="-2">
167
- <Callout.Text>{formErrors.form}</Callout.Text>
168
- </Callout.Root>
169
- ) : null}
170
-
171
- <Flex mt="5" gap="3" justify="end">
172
- <Dialog.Close>
173
- <SecondaryButton disabled={inviteUser.isPending}>
174
- Cancel
175
- </SecondaryButton>
176
- </Dialog.Close>
177
- <PrimaryButton
178
- form={formId}
179
- loading={inviteUser.isPending}
180
- disabled={rolesQuery.isPending || undefined}
181
- >
182
- Invite
183
- </PrimaryButton>
184
- </Flex>
185
- {/* mirror errors in a live region */}
186
- <VisuallyHidden asChild>
187
- <section aria-live="polite">{formErrors.form}</section>
188
- </VisuallyHidden>
189
- </DialogContent>
190
- </Dialog.Root>
191
- );
192
- }
193
-
194
- interface FormControlRenderProps {
195
- id: string;
196
- name: string;
197
- "aria-describedby": string | undefined;
198
- "aria-invalid"?: boolean;
199
- required: boolean | undefined;
200
- disabled: boolean | undefined;
201
- }
202
-
203
- function FormField({
204
- rootId,
205
- name,
206
- label,
207
- error,
208
- info,
209
- control,
210
- required,
211
- disabled,
212
- }: {
213
- rootId: string;
214
- name: string;
215
- label: string;
216
- error?: React.ReactNode;
217
- info?: React.ReactNode;
218
- control: (props: FormControlRenderProps) => React.ReactNode;
219
- required?: boolean;
220
- disabled?: boolean;
221
- }) {
222
- const fieldId = toId(rootId, name);
223
- const errorId = toId(rootId, name, "error");
224
- const infoId = toId(rootId, name, "info");
225
- return (
226
- <Flex direction="column" gap="1">
227
- <Label htmlFor={fieldId}>{label}</Label>
228
- {control({
229
- id: fieldId,
230
- name,
231
- "aria-describedby": (() => {
232
- const tags: string[] = [];
233
- if (error) {
234
- tags.push(errorId);
235
- }
236
- if (info) {
237
- tags.push(infoId);
238
- }
239
- if (tags.length === 0) {
240
- return undefined;
241
- }
242
- return tags.join(" ");
243
- })(),
244
- "aria-invalid": !!error || undefined,
245
- required: required || undefined,
246
- disabled: disabled || undefined,
247
- })}
248
-
249
- {error ? (
250
- <Text color="red" size="2" id={errorId}>
251
- {error}
252
- </Text>
253
- ) : null}
254
- {info ? (
255
- <Text color="gray" size="2" id={infoId} mt="1">
256
- {info}
257
- </Text>
258
- ) : null}
259
- </Flex>
260
- );
261
- }
262
-
263
- function toId(...parts: string[]) {
264
- return parts.join("-");
265
- }
266
-
267
- function getFormErrors(queryError: unknown) {
268
- const formErrors = {
269
- form: null as string | null,
270
- fields: {
271
- email: null as string | null,
272
- role: null as string | null,
273
- },
274
- };
275
-
276
- if (queryError) {
277
- if (!isErrorLike(queryError)) {
278
- return {
279
- ...formErrors,
280
- form: "An unexpected error occurred. Please try again.",
281
- };
282
- }
283
-
284
- switch (queryError.message.toLowerCase()) {
285
- case "user already exists":
286
- case "user already invited":
287
- case "invalid email":
288
- formErrors.fields.email = queryError.message;
289
- break;
290
- case "invalid role":
291
- formErrors.fields.role = queryError.message;
292
- break;
293
- default:
294
- // TODO handle more cases for various server errors
295
- formErrors.form =
296
- "There was an error inviting this user. Please refresh the page and try again.";
297
- break;
298
- }
299
- }
300
-
301
- return formErrors;
302
- }
303
-
304
- function useFormFieldFocusOnError(dialogId: string, queryError: unknown) {
305
- React.useEffect(() => {
306
- const fieldErrors = getFormErrors(queryError).fields;
307
- for (const [name, error] of Object.entries(fieldErrors)) {
308
- if (error) {
309
- const fieldElement = document.getElementById(toId(dialogId, name)) as
310
- | HTMLInputElement
311
- | HTMLButtonElement
312
- | null;
313
- if (fieldElement) {
314
- fieldElement?.focus();
315
- if ("select" in fieldElement) {
316
- fieldElement.select();
317
- }
318
- }
319
- break;
320
- }
321
- }
322
- }, [dialogId, queryError]);
323
- }
324
-
325
- function getDefaultRole(roles: MemberRole[]) {
326
- return roles.find((role) => role.default) || roles[0];
327
- }
@@ -1,82 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { AlertDialog, Flex } from "@radix-ui/themes";
5
- import { type ReactNode } from "react";
6
- import {
7
- AlertDialogContent,
8
- DestructiveButton,
9
- SecondaryButton,
10
- } from "./elements";
11
- import { getSessionsQueryKey, useRevokeAllSessions } from "../api";
12
- import { useQueryClient } from "@tanstack/react-query";
13
- import { SaveButton } from "./save-button";
14
-
15
- interface LogoutAllSessionsDialogProps extends AlertDialog.RootProps {
16
- children?: ReactNode;
17
- currentSessionId: string;
18
- }
19
-
20
- export function LogoutAllSessionsDialog({
21
- open,
22
- onOpenChange,
23
- children,
24
- currentSessionId,
25
- ...props
26
- }: LogoutAllSessionsDialogProps) {
27
- const client = useQueryClient();
28
-
29
- const revokeAllSessions = useRevokeAllSessions();
30
-
31
- const onSubmitForm = () => {
32
- revokeAllSessions.mutate({ data: { currentSessionId } });
33
- };
34
-
35
- const handleDone = React.useCallback(() => {
36
- onOpenChange?.(false);
37
-
38
- client.invalidateQueries({
39
- queryKey: getSessionsQueryKey(),
40
- });
41
- }, [client, onOpenChange]);
42
-
43
- return (
44
- <AlertDialog.Root open={open} onOpenChange={onOpenChange} {...props}>
45
- <AlertDialogContent maxWidth="480px">
46
- <AlertDialog.Title>Sign out of all other devices?</AlertDialog.Title>
47
- <AlertDialog.Description>
48
- You will be logged out of all other active sessions on other devices,
49
- except this one.
50
- </AlertDialog.Description>
51
-
52
- <Flex gap="3" justify="end" mt="5" asChild>
53
- <form
54
- onSubmit={(event) => {
55
- event.preventDefault();
56
- onSubmitForm();
57
- }}
58
- >
59
- <AlertDialog.Cancel>
60
- <SecondaryButton
61
- disabled={
62
- revokeAllSessions.isPending || revokeAllSessions.isSuccess
63
- }
64
- >
65
- Cancel
66
- </SecondaryButton>
67
- </AlertDialog.Cancel>
68
-
69
- <SaveButton
70
- asChild
71
- loading={revokeAllSessions.isPending}
72
- done={revokeAllSessions.isSuccess}
73
- onDone={handleDone}
74
- >
75
- <DestructiveButton type="submit">Sign out</DestructiveButton>
76
- </SaveButton>
77
- </form>
78
- </Flex>
79
- </AlertDialogContent>
80
- </AlertDialog.Root>
81
- );
82
- }
@@ -1,85 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { AlertDialog, Flex, Strong } from "@radix-ui/themes";
5
- import { type ReactNode } from "react";
6
- import {
7
- AlertDialogContent,
8
- DestructiveButton,
9
- SecondaryButton,
10
- } from "./elements";
11
- import { ActiveSession, getSessionsQueryKey, useRevokeSession } from "../api";
12
- import { useQueryClient } from "@tanstack/react-query";
13
- import { SaveButton } from "./save-button";
14
- import { parseUserAgent } from "./utils";
15
-
16
- interface LogoutDialogProps extends AlertDialog.RootProps {
17
- children?: ReactNode;
18
- session: ActiveSession;
19
- }
20
-
21
- export function LogoutDialog({
22
- children,
23
- session,
24
- open,
25
- onOpenChange,
26
- ...props
27
- }: LogoutDialogProps) {
28
- const client = useQueryClient();
29
- const userAgent = parseUserAgent(session.userAgent);
30
- const device = userAgent.pretty;
31
-
32
- const revokeSession = useRevokeSession();
33
-
34
- const handleDone = React.useCallback(() => {
35
- onOpenChange?.(false);
36
-
37
- client.invalidateQueries({
38
- queryKey: getSessionsQueryKey(),
39
- exact: false,
40
- });
41
- }, [onOpenChange, client]);
42
-
43
- const onSubmitForm = () => {
44
- revokeSession.mutate({
45
- sessionId: session.id,
46
- });
47
- };
48
-
49
- return (
50
- <AlertDialog.Root open={open} onOpenChange={onOpenChange} {...props}>
51
- <AlertDialogContent maxWidth="480px">
52
- <AlertDialog.Title>Sign out of device?</AlertDialog.Title>
53
- <AlertDialog.Description>
54
- You will be signed out of <Strong>{device}.</Strong>
55
- </AlertDialog.Description>
56
-
57
- <Flex gap="3" justify="end" mt="5" asChild>
58
- <form
59
- onSubmit={(event) => {
60
- event.preventDefault();
61
- onSubmitForm();
62
- }}
63
- >
64
- <AlertDialog.Cancel>
65
- <SecondaryButton
66
- disabled={revokeSession.isPending || revokeSession.isSuccess}
67
- >
68
- Cancel
69
- </SecondaryButton>
70
- </AlertDialog.Cancel>
71
-
72
- <SaveButton
73
- asChild
74
- loading={revokeSession.isPending}
75
- done={revokeSession.isSuccess}
76
- onDone={handleDone}
77
- >
78
- <DestructiveButton type="submit">Sign out</DestructiveButton>
79
- </SaveButton>
80
- </form>
81
- </Flex>
82
- </AlertDialogContent>
83
- </AlertDialog.Root>
84
- );
85
- }
@@ -1,39 +0,0 @@
1
- import * as React from "react";
2
- import { Text } from "@radix-ui/themes";
3
- import { MarginProps } from "@radix-ui/themes/props";
4
- import clsx from "clsx";
5
- import { namespaceClassNames } from "./utils";
6
-
7
- type TextProps = React.ComponentPropsWithoutRef<typeof Text>;
8
-
9
- type MarkerOwnProps = {
10
- color?: "gray" | "purple" | "blue" | "green" | "yellow" | "red";
11
- highContrast?: boolean;
12
- size?: TextProps["size"];
13
- };
14
-
15
- interface MarkerProps
16
- extends Omit<React.ComponentPropsWithRef<"span">, "color">,
17
- MarkerOwnProps,
18
- MarginProps {}
19
-
20
- export const Marker = React.forwardRef<HTMLSpanElement, MarkerProps>(
21
- function Marker(
22
- { children, className, highContrast, ...props },
23
- forwardedRef,
24
- ) {
25
- return (
26
- <Text
27
- ref={forwardedRef}
28
- className={clsx(className, namespaceClassNames("marker"))}
29
- {...props}
30
- >
31
- <span className={namespaceClassNames("marker-circle")}>
32
- <span className={namespaceClassNames("marker-content")}>
33
- {children}
34
- </span>
35
- </span>
36
- </Text>
37
- );
38
- },
39
- );
@@ -1,138 +0,0 @@
1
- import * as React from "react";
2
-
3
- const GitHub = React.forwardRef<
4
- SVGSVGElement,
5
- React.ComponentPropsWithoutRef<"svg">
6
- >((props, forwardedRef) => (
7
- <svg
8
- ref={forwardedRef}
9
- fill="none"
10
- height="16"
11
- viewBox="0 0 15 15"
12
- width="16"
13
- xmlns="http://www.w3.org/2000/svg"
14
- {...props}
15
- >
16
- <path
17
- clipRule="evenodd"
18
- d="M7.49933 0.25C3.49635 0.25 0.25 3.49593 0.25 7.50024C0.25 10.703 2.32715 13.4206 5.2081 14.3797C5.57084 14.446 5.70302 14.2222 5.70302 14.0299C5.70302 13.8576 5.69679 13.4019 5.69323 12.797C3.67661 13.235 3.25112 11.825 3.25112 11.825C2.92132 10.9874 2.44599 10.7644 2.44599 10.7644C1.78773 10.3149 2.49584 10.3238 2.49584 10.3238C3.22353 10.375 3.60629 11.0711 3.60629 11.0711C4.25298 12.1788 5.30335 11.8588 5.71638 11.6732C5.78225 11.205 5.96962 10.8854 6.17658 10.7043C4.56675 10.5209 2.87415 9.89918 2.87415 7.12104C2.87415 6.32925 3.15677 5.68257 3.62053 5.17563C3.54576 4.99226 3.29697 4.25521 3.69174 3.25691C3.69174 3.25691 4.30015 3.06196 5.68522 3.99973C6.26337 3.83906 6.8838 3.75895 7.50022 3.75583C8.1162 3.75895 8.73619 3.83906 9.31523 3.99973C10.6994 3.06196 11.3069 3.25691 11.3069 3.25691C11.7026 4.25521 11.4538 4.99226 11.3795 5.17563C11.8441 5.68257 12.1245 6.32925 12.1245 7.12104C12.1245 9.9063 10.4292 10.5192 8.81452 10.6985C9.07444 10.9224 9.30633 11.3648 9.30633 12.0413C9.30633 13.0102 9.29742 13.7922 9.29742 14.0299C9.29742 14.2239 9.42828 14.4496 9.79591 14.3788C12.6746 13.4179 14.75 10.7025 14.75 7.50024C14.75 3.49593 11.5036 0.25 7.49933 0.25Z"
19
- fill="currentColor"
20
- fillRule="evenodd"
21
- ></path>
22
- </svg>
23
- ));
24
-
25
- GitHub.displayName = "GitHub";
26
-
27
- const Google = React.forwardRef<
28
- SVGSVGElement,
29
- React.ComponentPropsWithoutRef<"svg">
30
- >((props, forwardedRef) => (
31
- <svg
32
- ref={forwardedRef}
33
- fill="none"
34
- height="15"
35
- viewBox="0 0 16 16"
36
- width="15"
37
- xmlns="http://www.w3.org/2000/svg"
38
- {...props}
39
- >
40
- <g>
41
- <path
42
- d="M15.83 8.18C15.83 7.65333 15.7833 7.15333 15.7033 6.66667H8.17V9.67333H12.4833C12.29 10.66 11.7233 11.4933 10.8833 12.06V14.06H13.4567C14.9633 12.6667 15.83 10.6133 15.83 8.18Z"
43
- fill="#4285F4"
44
- />
45
- <path
46
- d="M8.17 16C10.33 16 12.1367 15.28 13.4567 14.06L10.8833 12.06C10.1633 12.54 9.25 12.8333 8.17 12.8333C6.08334 12.8333 4.31667 11.4267 3.68334 9.52667H1.03V11.5867C2.34334 14.2 5.04334 16 8.17 16Z"
47
- fill="#34A853"
48
- />
49
- <path
50
- d="M3.68334 9.52667C3.51667 9.04667 3.43 8.53333 3.43 8C3.43 7.46667 3.52334 6.95334 3.68334 6.47334V4.41334H1.03C0.483335 5.49334 0.170002 6.70667 0.170002 8C0.170002 9.29333 0.483335 10.5067 1.03 11.5867L3.68334 9.52667Z"
51
- fill="#FBBC05"
52
- />
53
- <path
54
- d="M8.17 3.16667C9.35 3.16667 10.4033 3.57334 11.2367 4.36667L13.5167 2.08667C12.1367 0.793334 10.33 0 8.17 0C5.04334 0 2.34334 1.8 1.03 4.41334L3.68334 6.47334C4.31667 4.57334 6.08334 3.16667 8.17 3.16667Z"
55
- fill="#EA4335"
56
- />
57
- </g>
58
- </svg>
59
- ));
60
-
61
- Google.displayName = "Google";
62
-
63
- const Microsoft = React.forwardRef<
64
- SVGSVGElement,
65
- React.ComponentPropsWithoutRef<"svg">
66
- >((props, forwardedRef) => (
67
- <svg
68
- ref={forwardedRef}
69
- fill="none"
70
- height="15"
71
- viewBox="0 0 15 15"
72
- width="15"
73
- xmlns="http://www.w3.org/2000/svg"
74
- {...props}
75
- >
76
- <g>
77
- <path d="M0 0H7L7 7H-4.76837e-07L0 0Z" fill="#F35325" />
78
- <path d="M8 0H15V7H8L8 0Z" fill="#81BC06" />
79
- <path d="M0 8H7V15H0V8Z" fill="#05A6F0" />
80
- <path d="M8 8L15 8V15L8 15V8Z" fill="#FFBA08" />
81
- </g>
82
- </svg>
83
- ));
84
-
85
- Microsoft.displayName = "Microsoft";
86
-
87
- const Apple = React.forwardRef<
88
- SVGSVGElement,
89
- React.ComponentPropsWithoutRef<"svg">
90
- >(({ style, ...props }, forwardedRef) => (
91
- <svg
92
- ref={forwardedRef}
93
- fill="none"
94
- height="15"
95
- style={{ overflow: "visible", ...style }}
96
- viewBox="0 0 15 15"
97
- width="15"
98
- xmlns="http://www.w3.org/2000/svg"
99
- {...props}
100
- >
101
- <path
102
- d="M14.219 3.33667C14.1169 3.41674 12.3137 4.44312 12.3137 6.72534C12.3137 9.3651 14.6082 10.299 14.6769 10.3221C14.6663 10.379 14.3124 11.601 13.4671 12.8462C12.7134 13.942 11.9263 15.0359 10.7288 15.0359C9.53134 15.0359 9.22317 14.3333 7.84081 14.3333C6.49366 14.3333 6.01469 15.0591 4.91935 15.0591C3.82401 15.0591 3.05978 14.0451 2.18104 12.8C1.1632 11.3378 0.34082 9.06625 0.34082 6.91034C0.34082 3.45232 2.56668 1.61835 4.75732 1.61835C5.92133 1.61835 6.89164 2.39036 7.62246 2.39036C8.31802 2.39036 9.40277 1.5721 10.7271 1.5721C11.2289 1.5721 13.0321 1.61835 14.219 3.33667ZM10.0984 0.108136C10.646 -0.548247 11.0334 -1.459 11.0334 -2.36975C11.0334 -2.49605 11.0229 -2.62412 11 -2.72729C10.1089 -2.6935 9.04883 -2.12783 8.40959 -1.37895C7.90772 -0.802617 7.43931 0.108136 7.43931 1.03134C7.43931 1.1701 7.46218 1.30883 7.47277 1.35331C7.52909 1.36398 7.62067 1.37643 7.71224 1.37643C8.51172 1.37643 9.51724 0.835672 10.0984 0.108136Z"
103
- fill="currentColor"
104
- />
105
- </svg>
106
- ));
107
-
108
- Apple.displayName = "Apple";
109
-
110
- export const getOAuthIcon = (account: string) => {
111
- switch (account) {
112
- case "GithubOAuth":
113
- return GitHub;
114
- case "GoogleOAuth":
115
- return Google;
116
- case "MicrosoftOAuth":
117
- return Microsoft;
118
- case "AppleOAuth":
119
- return Apple;
120
- default:
121
- throw new Error(`Unknown OAuth account type: ${account}`);
122
- }
123
- };
124
-
125
- export const getOAuthName = (account: string) => {
126
- switch (account) {
127
- case "GithubOAuth":
128
- return "GitHub";
129
- case "GoogleOAuth":
130
- return "Google";
131
- case "MicrosoftOAuth":
132
- return "Microsoft";
133
- case "AppleOAuth":
134
- return "Apple";
135
- default:
136
- throw new Error(`Unknown OAuth account type: ${account}`);
137
- }
138
- };