@stackframe/stack 2.8.12 → 2.8.17
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.
- package/CHANGELOG.md +46 -0
- package/dist/components/api-key-dialogs.js +5 -4
- package/dist/components/api-key-dialogs.js.map +1 -1
- package/dist/components/credential-sign-in.js +4 -4
- package/dist/components/credential-sign-up.js +3 -3
- package/dist/components/elements/maybe-full-page.js +1 -1
- package/dist/components/elements/sidebar-layout.js +1 -1
- package/dist/components/magic-link-sign-in.js +3 -3
- package/dist/components/message-cards/known-error-message-card.js +2 -2
- package/dist/components/message-cards/message-card.js +1 -1
- package/dist/components/message-cards/predefined-message-card.js +3 -3
- package/dist/components/oauth-button-group.js +2 -2
- package/dist/components/oauth-button.js +27 -16
- package/dist/components/oauth-button.js.map +1 -1
- package/dist/components/passkey-button.js +2 -2
- package/dist/components/profile-image-editor.js +87 -34
- package/dist/components/profile-image-editor.js.map +1 -1
- package/dist/components/selected-team-switcher.js +60 -14
- package/dist/components/selected-team-switcher.js.map +1 -1
- package/dist/components/team-icon.js +4 -0
- package/dist/components/team-icon.js.map +1 -1
- package/dist/components/{iframe-preventer.js → use-in-iframe.js} +9 -19
- package/dist/components/use-in-iframe.js.map +1 -0
- package/dist/components/user-button.js +41 -8
- package/dist/components/user-button.js.map +1 -1
- package/dist/components-page/account-settings/active-sessions/active-sessions-page.js +57 -12
- package/dist/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
- package/dist/components-page/account-settings/api-keys/api-keys-page.js +100 -12
- package/dist/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
- package/dist/components-page/account-settings/editable-text.js +1 -1
- package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js +12 -12
- package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
- package/dist/components-page/account-settings/email-and-auth/emails-section.js +14 -5
- package/dist/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
- package/dist/components-page/account-settings/email-and-auth/mfa-section.js +18 -5
- package/dist/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
- package/dist/components-page/account-settings/email-and-auth/otp-section.js +18 -5
- package/dist/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
- package/dist/components-page/account-settings/email-and-auth/passkey-section.js +19 -6
- package/dist/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
- package/dist/components-page/account-settings/email-and-auth/password-section.js +20 -7
- package/dist/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
- package/dist/components-page/account-settings/notifications/notifications-page.js +59 -0
- package/dist/components-page/account-settings/notifications/notifications-page.js.map +1 -0
- package/dist/components-page/account-settings/profile-page/profile-page.js +18 -8
- package/dist/components-page/account-settings/profile-page/profile-page.js.map +1 -1
- package/dist/components-page/account-settings/settings/delete-account-section.js +19 -10
- package/dist/components-page/account-settings/settings/delete-account-section.js.map +1 -1
- package/dist/components-page/account-settings/settings/settings-page.js +6 -6
- package/dist/components-page/account-settings/settings/settings-page.js.map +1 -1
- package/dist/components-page/account-settings/settings/sign-out-section.js +15 -6
- package/dist/components-page/account-settings/settings/sign-out-section.js.map +1 -1
- package/dist/components-page/account-settings/teams/leave-team-section.js +3 -3
- package/dist/components-page/account-settings/teams/team-api-keys-section.js +5 -5
- package/dist/components-page/account-settings/teams/team-creation-page.js +19 -10
- package/dist/components-page/account-settings/teams/team-creation-page.js.map +1 -1
- package/dist/components-page/account-settings/teams/team-display-name-section.js +4 -4
- package/dist/components-page/account-settings/teams/team-member-invitation-section.js +4 -4
- package/dist/components-page/account-settings/teams/team-member-list-section.js +3 -3
- package/dist/components-page/account-settings/teams/team-page.js +8 -8
- package/dist/components-page/account-settings/teams/team-profile-image-section.js +4 -4
- package/dist/components-page/account-settings/teams/team-profile-user-section.js +4 -4
- package/dist/components-page/account-settings.js +43 -21
- package/dist/components-page/account-settings.js.map +1 -1
- package/dist/components-page/auth-page.js +11 -12
- package/dist/components-page/auth-page.js.map +1 -1
- package/dist/components-page/cli-auth-confirm.js +3 -3
- package/dist/components-page/email-verification.js +3 -3
- package/dist/components-page/error-page.js +6 -6
- package/dist/components-page/error-page.js.map +1 -1
- package/dist/components-page/forgot-password.js +6 -6
- package/dist/components-page/magic-link-callback.js +4 -4
- package/dist/components-page/mfa.js +190 -0
- package/dist/components-page/mfa.js.map +1 -0
- package/dist/components-page/oauth-callback.js +4 -4
- package/dist/components-page/password-reset.js +6 -6
- package/dist/components-page/sign-in.js +3 -2
- package/dist/components-page/sign-in.js.map +1 -1
- package/dist/components-page/sign-out.js +2 -2
- package/dist/components-page/sign-up.js +1 -1
- package/dist/components-page/stack-handler.js +25 -14
- package/dist/components-page/stack-handler.js.map +1 -1
- package/dist/components-page/team-creation.js +4 -4
- package/dist/components-page/team-invitation.js +3 -3
- package/dist/esm/components/api-key-dialogs.js +5 -4
- package/dist/esm/components/api-key-dialogs.js.map +1 -1
- package/dist/esm/components/credential-sign-in.js +4 -4
- package/dist/esm/components/credential-sign-up.js +3 -3
- package/dist/esm/components/elements/maybe-full-page.js +1 -1
- package/dist/esm/components/elements/sidebar-layout.js +1 -1
- package/dist/esm/components/magic-link-sign-in.js +3 -3
- package/dist/esm/components/message-cards/known-error-message-card.js +2 -2
- package/dist/esm/components/message-cards/message-card.js +1 -1
- package/dist/esm/components/message-cards/predefined-message-card.js +3 -3
- package/dist/esm/components/oauth-button-group.js +2 -2
- package/dist/esm/components/oauth-button.js +28 -17
- package/dist/esm/components/oauth-button.js.map +1 -1
- package/dist/esm/components/passkey-button.js +2 -2
- package/dist/esm/components/profile-image-editor.js +86 -34
- package/dist/esm/components/profile-image-editor.js.map +1 -1
- package/dist/esm/components/selected-team-switcher.js +60 -14
- package/dist/esm/components/selected-team-switcher.js.map +1 -1
- package/dist/esm/components/team-icon.js +4 -0
- package/dist/esm/components/team-icon.js.map +1 -1
- package/dist/esm/components/use-in-iframe.js +18 -0
- package/dist/esm/components/use-in-iframe.js.map +1 -0
- package/dist/esm/components/user-button.js +41 -8
- package/dist/esm/components/user-button.js.map +1 -1
- package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js +57 -12
- package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
- package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js +100 -12
- package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
- package/dist/esm/components-page/account-settings/editable-text.js +1 -1
- package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js +12 -12
- package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
- package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js +14 -5
- package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
- package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js +18 -5
- package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
- package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js +18 -5
- package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
- package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js +19 -6
- package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
- package/dist/esm/components-page/account-settings/email-and-auth/password-section.js +20 -7
- package/dist/esm/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
- package/dist/esm/components-page/account-settings/notifications/notifications-page.js +34 -0
- package/dist/esm/components-page/account-settings/notifications/notifications-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/profile-page/profile-page.js +18 -8
- package/dist/esm/components-page/account-settings/profile-page/profile-page.js.map +1 -1
- package/dist/esm/components-page/account-settings/settings/delete-account-section.js +19 -10
- package/dist/esm/components-page/account-settings/settings/delete-account-section.js.map +1 -1
- package/dist/esm/components-page/account-settings/settings/settings-page.js +6 -6
- package/dist/esm/components-page/account-settings/settings/settings-page.js.map +1 -1
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js +15 -6
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js.map +1 -1
- package/dist/esm/components-page/account-settings/teams/leave-team-section.js +3 -3
- package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js +5 -5
- package/dist/esm/components-page/account-settings/teams/team-creation-page.js +19 -10
- package/dist/esm/components-page/account-settings/teams/team-creation-page.js.map +1 -1
- package/dist/esm/components-page/account-settings/teams/team-display-name-section.js +4 -4
- package/dist/esm/components-page/account-settings/teams/team-member-invitation-section.js +4 -4
- package/dist/esm/components-page/account-settings/teams/team-member-list-section.js +3 -3
- package/dist/esm/components-page/account-settings/teams/team-page.js +8 -8
- package/dist/esm/components-page/account-settings/teams/team-profile-image-section.js +4 -4
- package/dist/esm/components-page/account-settings/teams/team-profile-user-section.js +4 -4
- package/dist/esm/components-page/account-settings.js +43 -21
- package/dist/esm/components-page/account-settings.js.map +1 -1
- package/dist/esm/components-page/auth-page.js +11 -12
- package/dist/esm/components-page/auth-page.js.map +1 -1
- package/dist/esm/components-page/cli-auth-confirm.js +3 -3
- package/dist/esm/components-page/email-verification.js +3 -3
- package/dist/esm/components-page/error-page.js +6 -6
- package/dist/esm/components-page/error-page.js.map +1 -1
- package/dist/esm/components-page/forgot-password.js +6 -6
- package/dist/esm/components-page/magic-link-callback.js +4 -4
- package/dist/esm/components-page/mfa.js +174 -0
- package/dist/esm/components-page/mfa.js.map +1 -0
- package/dist/esm/components-page/oauth-callback.js +4 -4
- package/dist/esm/components-page/password-reset.js +6 -6
- package/dist/esm/components-page/sign-in.js +3 -2
- package/dist/esm/components-page/sign-in.js.map +1 -1
- package/dist/esm/components-page/sign-out.js +2 -2
- package/dist/esm/components-page/sign-up.js +1 -1
- package/dist/esm/components-page/stack-handler.js +25 -14
- package/dist/esm/components-page/stack-handler.js.map +1 -1
- package/dist/esm/components-page/team-creation.js +4 -4
- package/dist/esm/components-page/team-invitation.js +3 -3
- package/dist/esm/generated/global-css.js +1 -1
- package/dist/esm/generated/global-css.js.map +1 -1
- package/dist/esm/generated/quetzal-translations.js +3616 -2364
- package/dist/esm/generated/quetzal-translations.js.map +1 -1
- package/dist/esm/index.js +22 -22
- package/dist/esm/lib/auth.js +2 -2
- package/dist/esm/lib/cookie.js +1 -129
- package/dist/esm/lib/cookie.js.map +1 -1
- package/dist/esm/lib/hooks.js +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +11 -8
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +79 -21
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js +2 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/index.js +3 -3
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +34 -8
- package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/index.js +3 -3
- package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/client-app.js +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/server-app.js +1 -1
- package/dist/esm/lib/stack-app/common.js.map +1 -1
- package/dist/esm/lib/stack-app/contact-channels/index.js.map +1 -1
- package/dist/esm/lib/stack-app/index.js +2 -2
- package/dist/esm/lib/stack-app/internal-api-keys/index.js.map +1 -1
- package/dist/esm/lib/stack-app/notification-categories/index.js +1 -0
- package/dist/esm/lib/stack-app/notification-categories/index.js.map +1 -0
- package/dist/esm/lib/stack-app/users/index.js.map +1 -1
- package/dist/esm/lib/translations.js +1 -1
- package/dist/esm/providers/stack-provider-client.js +2 -2
- package/dist/esm/providers/stack-provider.js +3 -3
- package/dist/esm/providers/theme-provider.js +3 -3
- package/dist/esm/providers/translation-provider.js +2 -2
- package/dist/esm/utils/browser-script.js +1 -1
- package/dist/generated/global-css.js +1 -1
- package/dist/generated/global-css.js.map +1 -1
- package/dist/generated/quetzal-translations.js +3616 -2364
- package/dist/generated/quetzal-translations.js.map +1 -1
- package/dist/index.d.mts +91 -6
- package/dist/index.d.ts +91 -6
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -1
- package/dist/lib/auth.js +2 -2
- package/dist/lib/cookie.js +4 -132
- package/dist/lib/cookie.js.map +1 -1
- package/dist/lib/hooks.js +1 -1
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +11 -8
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js +79 -21
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js +2 -1
- package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/index.js +3 -3
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js +34 -8
- package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/index.js +3 -3
- package/dist/lib/stack-app/apps/interfaces/admin-app.js +1 -1
- package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/client-app.js +1 -1
- package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/server-app.js +1 -1
- package/dist/lib/stack-app/common.js.map +1 -1
- package/dist/lib/stack-app/contact-channels/index.js.map +1 -1
- package/dist/lib/stack-app/index.js +2 -2
- package/dist/lib/stack-app/internal-api-keys/index.js.map +1 -1
- package/dist/lib/stack-app/notification-categories/index.js +19 -0
- package/dist/lib/stack-app/notification-categories/index.js.map +1 -0
- package/dist/lib/stack-app/users/index.js.map +1 -1
- package/dist/lib/translations.js +1 -1
- package/dist/providers/stack-provider-client.js +2 -2
- package/dist/providers/stack-provider.js +3 -3
- package/dist/providers/theme-provider.js +3 -3
- package/dist/providers/translation-provider.js +2 -2
- package/dist/utils/browser-script.js +1 -1
- package/package.json +5 -5
- package/dist/components/iframe-preventer.js.map +0 -1
- package/dist/esm/components/iframe-preventer.js +0 -28
- package/dist/esm/components/iframe-preventer.js.map +0 -1
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// src/components/passkey-button.tsx
|
|
5
5
|
import { Button } from "@stackframe/stack-ui";
|
|
6
6
|
import { useId } from "react";
|
|
7
|
-
import { useStackApp } from "
|
|
8
|
-
import { useTranslation } from "../lib/translations";
|
|
7
|
+
import { useStackApp } from "../index.js";
|
|
8
|
+
import { useTranslation } from "../lib/translations.js";
|
|
9
9
|
import { KeyRound } from "lucide-react";
|
|
10
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
function PasskeyButton({
|
|
@@ -4,10 +4,10 @@ import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/
|
|
|
4
4
|
import { Button, Slider, Typography } from "@stackframe/stack-ui";
|
|
5
5
|
import imageCompression from "browser-image-compression";
|
|
6
6
|
import { Upload } from "lucide-react";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import { useTranslation } from "../lib/translations";
|
|
10
|
-
import { UserAvatar } from "./elements/user-avatar";
|
|
7
|
+
import { useCallback, useState } from "react";
|
|
8
|
+
import Cropper from "react-easy-crop";
|
|
9
|
+
import { useTranslation } from "../lib/translations.js";
|
|
10
|
+
import { UserAvatar } from "./elements/user-avatar.js";
|
|
11
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
async function checkImageUrl(url) {
|
|
13
13
|
try {
|
|
@@ -18,17 +18,64 @@ async function checkImageUrl(url) {
|
|
|
18
18
|
return false;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
var createImage = (url) => new Promise((resolve, reject) => {
|
|
22
|
+
const image = new Image();
|
|
23
|
+
image.addEventListener("load", () => resolve(image));
|
|
24
|
+
image.addEventListener("error", (error) => reject(error));
|
|
25
|
+
image.setAttribute("crossOrigin", "anonymous");
|
|
26
|
+
image.src = url;
|
|
27
|
+
});
|
|
28
|
+
async function getCroppedImg(imageSrc, pixelCrop) {
|
|
29
|
+
const image = await createImage(imageSrc);
|
|
30
|
+
const canvas = document.createElement("canvas");
|
|
31
|
+
const ctx = canvas.getContext("2d");
|
|
32
|
+
if (!ctx) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const safeCrop = {
|
|
36
|
+
x: Math.max(0, pixelCrop.x),
|
|
37
|
+
y: Math.max(0, pixelCrop.y),
|
|
38
|
+
width: Math.max(1, pixelCrop.width),
|
|
39
|
+
height: Math.max(1, pixelCrop.height)
|
|
40
|
+
};
|
|
41
|
+
canvas.width = safeCrop.width;
|
|
42
|
+
canvas.height = safeCrop.height;
|
|
43
|
+
ctx.drawImage(
|
|
44
|
+
image,
|
|
45
|
+
safeCrop.x,
|
|
46
|
+
safeCrop.y,
|
|
47
|
+
safeCrop.width,
|
|
48
|
+
safeCrop.height,
|
|
49
|
+
0,
|
|
50
|
+
0,
|
|
51
|
+
safeCrop.width,
|
|
52
|
+
safeCrop.height
|
|
53
|
+
);
|
|
54
|
+
return canvas.toDataURL("image/jpeg");
|
|
55
|
+
}
|
|
21
56
|
function ProfileImageEditor(props) {
|
|
22
57
|
const { t } = useTranslation();
|
|
23
|
-
const cropRef = useRef(null);
|
|
24
|
-
const [slideValue, setSlideValue] = useState(1);
|
|
25
58
|
const [rawUrl, setRawUrl] = useState(null);
|
|
26
59
|
const [error, setError] = useState(null);
|
|
60
|
+
const [crop, setCrop] = useState({ x: 0, y: 0 });
|
|
61
|
+
const [zoom, setZoom] = useState(1);
|
|
62
|
+
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
|
|
27
63
|
function reset() {
|
|
28
|
-
setSlideValue(1);
|
|
29
64
|
setRawUrl(null);
|
|
30
65
|
setError(null);
|
|
66
|
+
setCrop({ x: 0, y: 0 });
|
|
67
|
+
setZoom(1);
|
|
68
|
+
setCroppedAreaPixels(null);
|
|
31
69
|
}
|
|
70
|
+
const onCropChange = useCallback((crop2) => {
|
|
71
|
+
setCrop(crop2);
|
|
72
|
+
}, []);
|
|
73
|
+
const onCropComplete = useCallback((croppedArea, croppedAreaPixels2) => {
|
|
74
|
+
setCroppedAreaPixels(croppedAreaPixels2);
|
|
75
|
+
}, []);
|
|
76
|
+
const onZoomChange = useCallback((zoom2) => {
|
|
77
|
+
setZoom(zoom2);
|
|
78
|
+
}, []);
|
|
32
79
|
function upload() {
|
|
33
80
|
const input = document.createElement("input");
|
|
34
81
|
input.type = "file";
|
|
@@ -65,28 +112,28 @@ function ProfileImageEditor(props) {
|
|
|
65
112
|
] });
|
|
66
113
|
}
|
|
67
114
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
68
|
-
/* @__PURE__ */ jsx(
|
|
69
|
-
|
|
115
|
+
/* @__PURE__ */ jsx("div", { className: "relative w-64 h-64", children: /* @__PURE__ */ jsx(
|
|
116
|
+
Cropper,
|
|
70
117
|
{
|
|
71
|
-
ref: cropRef,
|
|
72
118
|
image: rawUrl || props.user.profileImageUrl || "",
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
119
|
+
crop,
|
|
120
|
+
zoom,
|
|
121
|
+
aspect: 1,
|
|
122
|
+
cropShape: "round",
|
|
123
|
+
showGrid: false,
|
|
124
|
+
onCropChange,
|
|
125
|
+
onCropComplete,
|
|
126
|
+
onZoomChange
|
|
79
127
|
}
|
|
80
|
-
),
|
|
128
|
+
) }),
|
|
81
129
|
/* @__PURE__ */ jsx(
|
|
82
130
|
Slider,
|
|
83
131
|
{
|
|
84
132
|
min: 1,
|
|
85
|
-
max:
|
|
133
|
+
max: 3,
|
|
86
134
|
step: 0.1,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
onValueChange: (v) => setSlideValue(v[0])
|
|
135
|
+
value: [zoom],
|
|
136
|
+
onValueChange: (v) => onZoomChange(v[0])
|
|
90
137
|
}
|
|
91
138
|
),
|
|
92
139
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-2", children: [
|
|
@@ -94,18 +141,22 @@ function ProfileImageEditor(props) {
|
|
|
94
141
|
Button,
|
|
95
142
|
{
|
|
96
143
|
onClick: async () => {
|
|
97
|
-
if (
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
await imageCompression
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
144
|
+
if (rawUrl && croppedAreaPixels) {
|
|
145
|
+
const croppedImageUrl = await getCroppedImg(rawUrl, croppedAreaPixels);
|
|
146
|
+
if (croppedImageUrl) {
|
|
147
|
+
const compressedFile = await imageCompression(
|
|
148
|
+
await imageCompression.getFilefromDataUrl(croppedImageUrl, "profile-image"),
|
|
149
|
+
{
|
|
150
|
+
maxSizeMB: 0.1,
|
|
151
|
+
fileType: "image/jpeg"
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
const compressedUrl = await imageCompression.getDataUrlFromFile(compressedFile);
|
|
155
|
+
await props.onProfileImageUrlChange(compressedUrl);
|
|
156
|
+
reset();
|
|
157
|
+
} else {
|
|
158
|
+
setError(t("Could not crop image."));
|
|
159
|
+
}
|
|
109
160
|
}
|
|
110
161
|
},
|
|
111
162
|
children: t("Save")
|
|
@@ -124,6 +175,7 @@ function ProfileImageEditor(props) {
|
|
|
124
175
|
}
|
|
125
176
|
export {
|
|
126
177
|
ProfileImageEditor,
|
|
127
|
-
checkImageUrl
|
|
178
|
+
checkImageUrl,
|
|
179
|
+
getCroppedImg
|
|
128
180
|
};
|
|
129
181
|
//# sourceMappingURL=profile-image-editor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/profile-image-editor.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { fileToBase64 } from '@stackframe/stack-shared/dist/utils/base64';\nimport { runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { Button, Slider, Typography } from '@stackframe/stack-ui';\nimport imageCompression from 'browser-image-compression';\nimport { Upload } from 'lucide-react';\nimport { ComponentProps,
|
|
1
|
+
{"version":3,"sources":["../../../src/components/profile-image-editor.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { fileToBase64 } from '@stackframe/stack-shared/dist/utils/base64';\nimport { runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { Button, Slider, Typography } from '@stackframe/stack-ui';\nimport imageCompression from 'browser-image-compression';\nimport { Upload } from 'lucide-react';\nimport { ComponentProps, useCallback, useState } from 'react';\nimport Cropper, { Area } from 'react-easy-crop';\nimport { useTranslation } from '../lib/translations';\nimport { UserAvatar } from './elements/user-avatar';\n\nexport async function checkImageUrl(url: string){\n try {\n const res = await fetch(url, { method: 'HEAD' });\n const buff = await res.blob();\n return buff.type.startsWith('image/');\n } catch (e) {\n return false;\n }\n}\n\nconst createImage = (url: string): Promise<HTMLImageElement> =>\n new Promise((resolve, reject) => {\n const image = new Image();\n image.addEventListener('load', () => resolve(image));\n image.addEventListener('error', (error) => reject(error));\n image.setAttribute('crossOrigin', 'anonymous');\n image.src = url;\n });\n\nexport async function getCroppedImg(imageSrc: string, pixelCrop: Area): Promise<string | null> {\n const image = await createImage(imageSrc);\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n\n if (!ctx) {\n return null;\n }\n\n const safeCrop = {\n x: Math.max(0, pixelCrop.x),\n y: Math.max(0, pixelCrop.y),\n width: Math.max(1, pixelCrop.width),\n height: Math.max(1, pixelCrop.height),\n };\n\n canvas.width = safeCrop.width;\n canvas.height = safeCrop.height;\n\n ctx.drawImage(\n image,\n safeCrop.x,\n safeCrop.y,\n safeCrop.width,\n safeCrop.height,\n 0,\n 0,\n safeCrop.width,\n safeCrop.height\n );\n\n return canvas.toDataURL('image/jpeg');\n}\n\nexport function ProfileImageEditor(props: {\n user: NonNullable<ComponentProps<typeof UserAvatar>['user']>,\n onProfileImageUrlChange: (profileImageUrl: string | null) => void | Promise<void>,\n}) {\n const { t } = useTranslation();\n const [rawUrl, setRawUrl] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [crop, setCrop] = useState({ x: 0, y: 0 });\n const [zoom, setZoom] = useState(1);\n const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);\n\n function reset() {\n setRawUrl(null);\n setError(null);\n setCrop({ x: 0, y: 0 });\n setZoom(1);\n setCroppedAreaPixels(null);\n }\n\n const onCropChange = useCallback((crop: { x: number, y: number }) => {\n setCrop(crop);\n }, []);\n\n const onCropComplete = useCallback((croppedArea: Area, croppedAreaPixels: Area) => {\n setCroppedAreaPixels(croppedAreaPixels);\n }, []);\n\n const onZoomChange = useCallback((zoom: number) => {\n setZoom(zoom);\n }, []);\n\n\n function upload() {\n const input = document.createElement('input');\n input.type = 'file';\n input.onchange = (e) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (!file) return;\n runAsynchronouslyWithAlert(async () => {\n const rawUrl = await fileToBase64(file);\n if (await checkImageUrl(rawUrl)) {\n setRawUrl(rawUrl);\n setError(null);\n } else {\n setError(t('Invalid image'));\n }\n input.remove();\n });\n };\n input.click();\n }\n\n if (!rawUrl) {\n return <div className='flex flex-col'>\n <div className='cursor-pointer relative' onClick={upload}>\n <UserAvatar\n size={60}\n user={props.user}\n border\n />\n <div className='absolute top-0 left-0 h-[60px] w-[60px] bg-gray-500/20 backdrop-blur-sm items-center justify-center rounded-full flex opacity-0 hover:opacity-100 transition-opacity'>\n <div className='bg-background p-2 rounded-full'>\n <Upload className='h-5 w-5' />\n </div>\n </div>\n </div>\n {error && <Typography variant='destructive' type='label'>{error}</Typography>}\n </div>;\n }\n\n return (\n <div className='flex flex-col items-center gap-4'>\n <div className='relative w-64 h-64'>\n <Cropper\n image={rawUrl || props.user.profileImageUrl || \"\"}\n crop={crop}\n zoom={zoom}\n aspect={1}\n cropShape=\"round\"\n showGrid={false}\n onCropChange={onCropChange}\n onCropComplete={onCropComplete}\n onZoomChange={onZoomChange}\n />\n </div>\n <Slider\n min={1}\n max={3}\n step={0.1}\n value={[zoom]}\n onValueChange={(v) => onZoomChange(v[0])}\n />\n\n <div className='flex flex-row gap-2'>\n <Button\n onClick={async () => {\n if (rawUrl && croppedAreaPixels) {\n const croppedImageUrl = await getCroppedImg(rawUrl, croppedAreaPixels);\n if (croppedImageUrl) {\n const compressedFile = await imageCompression(\n await imageCompression.getFilefromDataUrl(croppedImageUrl, 'profile-image'),\n {\n maxSizeMB: 0.1,\n fileType: \"image/jpeg\",\n }\n );\n const compressedUrl = await imageCompression.getDataUrlFromFile(compressedFile);\n await props.onProfileImageUrlChange(compressedUrl);\n reset();\n } else {\n setError(t('Could not crop image.'));\n }\n }\n }}\n >\n {t('Save')}\n </Button>\n <Button\n variant=\"secondary\"\n onClick={reset}\n >\n {t('Cancel')}\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["crop","croppedAreaPixels","zoom","rawUrl"]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
// src/components/selected-team-switcher.tsx
|
|
5
|
+
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
|
5
6
|
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
|
6
7
|
import {
|
|
7
8
|
Button,
|
|
@@ -18,9 +19,9 @@ import {
|
|
|
18
19
|
} from "@stackframe/stack-ui";
|
|
19
20
|
import { PlusCircle, Settings } from "lucide-react";
|
|
20
21
|
import { Suspense, useEffect, useMemo } from "react";
|
|
21
|
-
import { useStackApp, useUser } from "
|
|
22
|
-
import { useTranslation } from "../lib/translations";
|
|
23
|
-
import { TeamIcon } from "./team-icon";
|
|
22
|
+
import { useStackApp, useUser } from "../index.js";
|
|
23
|
+
import { useTranslation } from "../lib/translations.js";
|
|
24
|
+
import { TeamIcon } from "./team-icon.js";
|
|
24
25
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
25
26
|
function SelectedTeamSwitcher(props) {
|
|
26
27
|
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Fallback, {}), children: /* @__PURE__ */ jsx(Inner, { ...props }) });
|
|
@@ -30,28 +31,51 @@ function Fallback() {
|
|
|
30
31
|
}
|
|
31
32
|
function Inner(props) {
|
|
32
33
|
const { t } = useTranslation();
|
|
33
|
-
const
|
|
34
|
-
const
|
|
34
|
+
const appFromHook = useStackApp();
|
|
35
|
+
const userFromHook = useUser();
|
|
36
|
+
const app = props.mockUser ? {
|
|
37
|
+
useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },
|
|
38
|
+
useNavigate: () => () => {
|
|
39
|
+
},
|
|
40
|
+
// Mock navigate function
|
|
41
|
+
urls: { accountSettings: "/account-settings" }
|
|
42
|
+
} : appFromHook;
|
|
43
|
+
const user = props.mockUser ? {
|
|
44
|
+
selectedTeam: props.mockUser.selectedTeam,
|
|
45
|
+
useTeams: () => props.mockTeams || [],
|
|
46
|
+
setSelectedTeam: async () => {
|
|
47
|
+
}
|
|
48
|
+
// Mock function
|
|
49
|
+
} : userFromHook;
|
|
35
50
|
const project = app.useProject();
|
|
36
51
|
const navigate = app.useNavigate();
|
|
37
52
|
const selectedTeam = user?.selectedTeam || props.selectedTeam;
|
|
38
53
|
const rawTeams = user?.useTeams();
|
|
39
54
|
const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);
|
|
40
55
|
useEffect(() => {
|
|
41
|
-
if (!props.noUpdateSelectedTeam && props.selectedTeam) {
|
|
56
|
+
if (!props.noUpdateSelectedTeam && props.selectedTeam && !props.mockUser) {
|
|
42
57
|
runAsynchronouslyWithAlert(user?.setSelectedTeam(props.selectedTeam));
|
|
43
58
|
}
|
|
44
|
-
}, [props.noUpdateSelectedTeam, props.selectedTeam]);
|
|
59
|
+
}, [props.noUpdateSelectedTeam, props.selectedTeam, props.mockUser]);
|
|
45
60
|
return /* @__PURE__ */ jsxs(
|
|
46
61
|
Select,
|
|
47
62
|
{
|
|
48
|
-
value: selectedTeam?.id,
|
|
63
|
+
value: selectedTeam?.id || (props.allowNull ? "null-sentinel" : void 0),
|
|
49
64
|
onValueChange: (value) => {
|
|
50
65
|
runAsynchronouslyWithAlert(async () => {
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
66
|
+
let team = null;
|
|
67
|
+
if (value !== "null-sentinel") {
|
|
68
|
+
team = teams?.find((team2) => team2.id === value) || null;
|
|
69
|
+
if (!team) {
|
|
70
|
+
throw new StackAssertionError("Team not found, this should not happen");
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
team = null;
|
|
74
|
+
}
|
|
75
|
+
if (props.onChange) {
|
|
76
|
+
props.onChange(team);
|
|
54
77
|
}
|
|
78
|
+
if (props.mockUser) return;
|
|
55
79
|
if (!props.noUpdateSelectedTeam) {
|
|
56
80
|
await user?.setSelectedTeam(team);
|
|
57
81
|
}
|
|
@@ -66,26 +90,48 @@ function Inner(props) {
|
|
|
66
90
|
user?.selectedTeam ? /* @__PURE__ */ jsxs(SelectGroup, { children: [
|
|
67
91
|
/* @__PURE__ */ jsx(SelectLabel, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
68
92
|
/* @__PURE__ */ jsx("span", { children: t("Current team") }),
|
|
69
|
-
/* @__PURE__ */ jsx(
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
Button,
|
|
95
|
+
{
|
|
96
|
+
variant: "ghost",
|
|
97
|
+
size: "icon",
|
|
98
|
+
className: "h-6 w-6",
|
|
99
|
+
onClick: () => {
|
|
100
|
+
if (!props.mockUser) {
|
|
101
|
+
navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
children: /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" })
|
|
105
|
+
}
|
|
106
|
+
)
|
|
70
107
|
] }) }),
|
|
71
108
|
/* @__PURE__ */ jsx(SelectItem, { value: user.selectedTeam.id, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
72
109
|
/* @__PURE__ */ jsx(TeamIcon, { team: user.selectedTeam }),
|
|
73
110
|
/* @__PURE__ */ jsx(Typography, { className: "max-w-40 truncate", children: user.selectedTeam.displayName })
|
|
74
111
|
] }) })
|
|
75
112
|
] }) : void 0,
|
|
113
|
+
props.allowNull && /* @__PURE__ */ jsx(SelectGroup, { children: /* @__PURE__ */ jsx(SelectItem, { value: "null-sentinel", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
114
|
+
/* @__PURE__ */ jsx(TeamIcon, { team: "personal" }),
|
|
115
|
+
/* @__PURE__ */ jsx(Typography, { className: "max-w-40 truncate", children: props.nullLabel || t("No team") })
|
|
116
|
+
] }) }) }),
|
|
76
117
|
teams?.length ? /* @__PURE__ */ jsxs(SelectGroup, { children: [
|
|
77
118
|
/* @__PURE__ */ jsx(SelectLabel, { children: t("Other teams") }),
|
|
78
119
|
teams.filter((team) => team.id !== user?.selectedTeam?.id).map((team) => /* @__PURE__ */ jsx(SelectItem, { value: team.id, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
79
120
|
/* @__PURE__ */ jsx(TeamIcon, { team }),
|
|
80
121
|
/* @__PURE__ */ jsx(Typography, { className: "max-w-64 truncate", children: team.displayName })
|
|
81
122
|
] }) }, team.id))
|
|
82
|
-
] }) :
|
|
123
|
+
] }) : null,
|
|
124
|
+
!teams?.length && !props.allowNull ? /* @__PURE__ */ jsx(SelectGroup, { children: /* @__PURE__ */ jsx(SelectLabel, { children: t("No teams yet") }) }) : null,
|
|
83
125
|
project.config.clientTeamCreationEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
84
126
|
/* @__PURE__ */ jsx(SelectSeparator, {}),
|
|
85
127
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
|
|
86
128
|
Button,
|
|
87
129
|
{
|
|
88
|
-
onClick: () =>
|
|
130
|
+
onClick: () => {
|
|
131
|
+
if (!props.mockUser) {
|
|
132
|
+
navigate(`${app.urls.accountSettings}#team-creation`);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
89
135
|
className: "w-full",
|
|
90
136
|
variant: "ghost",
|
|
91
137
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/selected-team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useEffect, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype SelectedTeamSwitcherProps = {\n urlMap?: (team: Team) => string,\n selectedTeam?: Team,\n noUpdateSelectedTeam?: boolean,\n};\n\nexport function SelectedTeamSwitcher(props: SelectedTeamSwitcherProps) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner(props: SelectedTeamSwitcherProps) {\n const { t } = useTranslation();\n const app = useStackApp();\n const user = useUser();\n const project = app.useProject();\n const navigate = app.useNavigate();\n const selectedTeam = user?.selectedTeam || props.selectedTeam;\n const rawTeams = user?.useTeams();\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n useEffect(() => {\n if (!props.noUpdateSelectedTeam && props.selectedTeam) {\n runAsynchronouslyWithAlert(user?.setSelectedTeam(props.selectedTeam));\n }\n }, [props.noUpdateSelectedTeam, props.selectedTeam]);\n\n return (\n <Select\n value={selectedTeam?.id}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n const team = teams?.find(team => team.id === value);\n if (!team) {\n throw new Error('Team not found, this should not happen');\n }\n\n if (!props.noUpdateSelectedTeam) {\n await user?.setSelectedTeam(team);\n }\n if (props.urlMap) {\n navigate(props.urlMap(team));\n }\n });\n }}\n >\n <SelectTrigger className=\"stack-scope max-w-64\">\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {user?.selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button variant='ghost' size='icon' className=\"h-6 w-6\" onClick={() => navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`)}>\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={user.selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={user.selectedTeam} />\n <Typography className=\"max-w-40 truncate\">{user.selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== user?.selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> :\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup>}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => navigate(`${app.urls.accountSettings}#team-creation`)}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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","names":["team"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/selected-team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useEffect, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype MockTeam = {\n id: string,\n displayName: string,\n profileImageUrl?: string | null,\n};\n\ntype SelectedTeamSwitcherProps<AllowNull extends boolean = false> = {\n urlMap?: (team: AllowNull extends true ? Team | null : Team) => string,\n selectedTeam?: Team,\n noUpdateSelectedTeam?: boolean,\n allowNull?: AllowNull,\n nullLabel?: string,\n onChange?: (team: AllowNull extends true ? Team | null : Team) => void,\n // Mock data props\n mockUser?: {\n selectedTeam?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function SelectedTeamSwitcher<AllowNull extends boolean = false>(props: SelectedTeamSwitcherProps<AllowNull>) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner<AllowNull extends boolean>(props: SelectedTeamSwitcherProps<AllowNull>) {\n const { t } = useTranslation();\n const appFromHook = useStackApp();\n const userFromHook = useUser();\n\n // Use mock data if provided, otherwise use real data\n const app = props.mockUser ? {\n useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },\n useNavigate: () => () => {}, // Mock navigate function\n urls: { accountSettings: '/account-settings' },\n } : appFromHook;\n\n const user = props.mockUser ? {\n selectedTeam: props.mockUser.selectedTeam,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const project = app.useProject();\n const navigate = app.useNavigate();\n const selectedTeam = user?.selectedTeam || props.selectedTeam;\n const rawTeams = user?.useTeams();\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n useEffect(() => {\n if (!props.noUpdateSelectedTeam && props.selectedTeam && !props.mockUser) {\n runAsynchronouslyWithAlert(user?.setSelectedTeam(props.selectedTeam));\n }\n }, [props.noUpdateSelectedTeam, props.selectedTeam, props.mockUser]);\n\n return (\n <Select\n value={selectedTeam?.id || (props.allowNull ? 'null-sentinel' : undefined)}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n let team: MockTeam | null = null;\n if (value !== 'null-sentinel') {\n team = teams?.find(team => team.id === value) || null;\n if (!team) {\n throw new StackAssertionError('Team not found, this should not happen');\n }\n } else {\n team = null;\n }\n\n // Call onChange callback if provided\n if (props.onChange) {\n props.onChange(team as Team);\n }\n\n // Skip actual navigation/updates in mock mode\n if (props.mockUser) return;\n\n if (!props.noUpdateSelectedTeam) {\n await user?.setSelectedTeam(team as Team);\n }\n if (props.urlMap) {\n navigate(props.urlMap(team as Team));\n }\n });\n }}\n >\n <SelectTrigger className=\"stack-scope max-w-64\">\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {user?.selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button\n variant='ghost'\n size='icon'\n className=\"h-6 w-6\"\n onClick={() => {\n // Skip navigation in mock mode\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={user.selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={user.selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{user.selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {props.allowNull && <SelectGroup>\n <SelectItem value=\"null-sentinel\">\n <div className=\"flex items-center gap-2\">\n <TeamIcon team='personal' />\n <Typography className=\"max-w-40 truncate\">{props.nullLabel || t('No team')}</Typography>\n </div>\n </SelectItem>\n </SelectGroup>}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== user?.selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team as Team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> : null}\n\n {!teams?.length && !props.allowNull ?\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup> : null}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n // Skip navigation in mock mode\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-creation`);\n }\n }}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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","names":["team"]}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// src/components/team-icon.tsx
|
|
2
2
|
import { Avatar, AvatarImage, Typography } from "@stackframe/stack-ui";
|
|
3
|
+
import { User2 } from "lucide-react";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
function TeamIcon(props) {
|
|
6
|
+
if (props.team === "personal") {
|
|
7
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-w-6 min-h-6 max-w-6 max-h-6 rounded bg-zinc-200", children: /* @__PURE__ */ jsx(User2, { className: "w-4 h-4" }) });
|
|
8
|
+
}
|
|
5
9
|
if (props.team.profileImageUrl) {
|
|
6
10
|
return /* @__PURE__ */ jsx(Avatar, { className: "min-w-6 min-h-6 max-w-6 max-h-6 rounded", children: /* @__PURE__ */ jsx(AvatarImage, { src: props.team.profileImageUrl, alt: props.team.displayName }) });
|
|
7
11
|
} else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/team-icon.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Avatar, AvatarImage, Typography } from \"@stackframe/stack-ui\";\nimport { Team } from \"..\";\n\nexport function TeamIcon(props: { team: Team }) {\n if (props.team.profileImageUrl) {\n return (\n <Avatar className=\"min-w-6 min-h-6 max-w-6 max-h-6 rounded\">\n <AvatarImage src={props.team.profileImageUrl} alt={props.team.displayName} />\n </Avatar>\n );\n } else {\n return (\n <div className=\"flex items-center justify-center min-w-6 min-h-6 max-w-6 max-h-6 rounded bg-zinc-200\">\n <Typography className=\"text-zinc-800 dark:text-zinc-800\">{props.team.displayName.slice(0, 1).toUpperCase()}</Typography>\n </div>\n );\n }\n}\n"],"mappings":";AAIA,SAAS,QAAQ,aAAa,kBAAkB;
|
|
1
|
+
{"version":3,"sources":["../../../src/components/team-icon.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Avatar, AvatarImage, Typography } from \"@stackframe/stack-ui\";\nimport { User2 } from \"lucide-react\";\nimport { Team } from \"..\";\n\nexport function TeamIcon(props: { team: Team | 'personal' }) {\n if (props.team === 'personal') {\n return (\n <div className=\"flex items-center justify-center min-w-6 min-h-6 max-w-6 max-h-6 rounded bg-zinc-200\">\n <User2 className=\"w-4 h-4\" />\n </div>\n );\n }\n if (props.team.profileImageUrl) {\n return (\n <Avatar className=\"min-w-6 min-h-6 max-w-6 max-h-6 rounded\">\n <AvatarImage src={props.team.profileImageUrl} alt={props.team.displayName} />\n </Avatar>\n );\n } else {\n return (\n <div className=\"flex items-center justify-center min-w-6 min-h-6 max-w-6 max-h-6 rounded bg-zinc-200\">\n <Typography className=\"text-zinc-800 dark:text-zinc-800\">{props.team.displayName.slice(0, 1).toUpperCase()}</Typography>\n </div>\n );\n }\n}\n"],"mappings":";AAIA,SAAS,QAAQ,aAAa,kBAAkB;AAChD,SAAS,aAAa;AAOd;AAJD,SAAS,SAAS,OAAoC;AAC3D,MAAI,MAAM,SAAS,YAAY;AAC7B,WACE,oBAAC,SAAI,WAAU,wFACb,8BAAC,SAAM,WAAU,WAAU,GAC7B;AAAA,EAEJ;AACA,MAAI,MAAM,KAAK,iBAAiB;AAC9B,WACE,oBAAC,UAAO,WAAU,2CAChB,8BAAC,eAAY,KAAK,MAAM,KAAK,iBAAiB,KAAK,MAAM,KAAK,aAAa,GAC7E;AAAA,EAEJ,OAAO;AACL,WACE,oBAAC,SAAI,WAAU,wFACb,8BAAC,cAAW,WAAU,oCAAoC,gBAAM,KAAK,YAAY,MAAM,GAAG,CAAC,EAAE,YAAY,GAAE,GAC7G;AAAA,EAEJ;AACF;","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/components/use-in-iframe.tsx
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
function useInIframe() {
|
|
7
|
+
const [isIframe, setIsIframe] = useState(false);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (window.self !== window.top) {
|
|
10
|
+
setIsIframe(true);
|
|
11
|
+
}
|
|
12
|
+
}, []);
|
|
13
|
+
return isIframe;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
useInIframe
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=use-in-iframe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/use-in-iframe.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { useEffect, useState } from \"react\";\n\nexport function useInIframe() {\n const [isIframe, setIsIframe] = useState(false);\n useEffect(() => {\n if (window.self !== window.top) {\n setIsIframe(true);\n }\n }, []);\n\n return isIframe;\n}\n"],"mappings":";;;AAMA,SAAS,WAAW,gBAAgB;AAE7B,SAAS,cAAc;AAC5B,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,YAAU,MAAM;AACd,QAAI,OAAO,SAAS,OAAO,KAAK;AAC9B,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;","names":[]}
|
|
@@ -6,9 +6,9 @@ import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/
|
|
|
6
6
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Skeleton, Typography } from "@stackframe/stack-ui";
|
|
7
7
|
import { CircleUser, LogIn, LogOut, SunMoon, UserPlus } from "lucide-react";
|
|
8
8
|
import { Suspense } from "react";
|
|
9
|
-
import { useStackApp, useUser } from "
|
|
10
|
-
import { useTranslation } from "../lib/translations";
|
|
11
|
-
import { UserAvatar } from "./elements/user-avatar";
|
|
9
|
+
import { useStackApp, useUser } from "../index.js";
|
|
10
|
+
import { useTranslation } from "../lib/translations.js";
|
|
11
|
+
import { UserAvatar } from "./elements/user-avatar.js";
|
|
12
12
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
function Item(props) {
|
|
14
14
|
return /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: () => runAsynchronouslyWithAlert(props.onClick), children: /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
@@ -20,7 +20,16 @@ function UserButton(props) {
|
|
|
20
20
|
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Skeleton, { className: "h-[34px] w-[34px] rounded-full stack-scope" }), children: /* @__PURE__ */ jsx(UserButtonInner, { ...props }) });
|
|
21
21
|
}
|
|
22
22
|
function UserButtonInner(props) {
|
|
23
|
-
const
|
|
23
|
+
const userFromHook = useUser();
|
|
24
|
+
const user = props.mockUser ? {
|
|
25
|
+
displayName: props.mockUser.displayName || "Mock User",
|
|
26
|
+
primaryEmail: props.mockUser.primaryEmail || "mock@example.com",
|
|
27
|
+
profileImageUrl: props.mockUser.profileImageUrl,
|
|
28
|
+
signOut: () => {
|
|
29
|
+
console.log("Mock sign out - no action taken in demo mode");
|
|
30
|
+
return Promise.resolve();
|
|
31
|
+
}
|
|
32
|
+
} : userFromHook;
|
|
24
33
|
return /* @__PURE__ */ jsx(UserButtonInnerInner, { ...props, user });
|
|
25
34
|
}
|
|
26
35
|
function UserButtonInnerInner(props) {
|
|
@@ -50,7 +59,13 @@ function UserButtonInnerInner(props) {
|
|
|
50
59
|
Item,
|
|
51
60
|
{
|
|
52
61
|
text: t("Account settings"),
|
|
53
|
-
onClick: async () =>
|
|
62
|
+
onClick: async () => {
|
|
63
|
+
if (props.mockUser) {
|
|
64
|
+
console.log("Mock account settings - no navigation in demo mode");
|
|
65
|
+
} else {
|
|
66
|
+
await app.redirectToAccountSettings();
|
|
67
|
+
}
|
|
68
|
+
},
|
|
54
69
|
icon: /* @__PURE__ */ jsx(CircleUser, { ...iconProps })
|
|
55
70
|
}
|
|
56
71
|
),
|
|
@@ -58,7 +73,13 @@ function UserButtonInnerInner(props) {
|
|
|
58
73
|
Item,
|
|
59
74
|
{
|
|
60
75
|
text: t("Sign in"),
|
|
61
|
-
onClick: async () =>
|
|
76
|
+
onClick: async () => {
|
|
77
|
+
if (props.mockUser) {
|
|
78
|
+
console.log("Mock sign in - no navigation in demo mode");
|
|
79
|
+
} else {
|
|
80
|
+
await app.redirectToSignIn();
|
|
81
|
+
}
|
|
82
|
+
},
|
|
62
83
|
icon: /* @__PURE__ */ jsx(LogIn, { ...iconProps })
|
|
63
84
|
}
|
|
64
85
|
),
|
|
@@ -66,7 +87,13 @@ function UserButtonInnerInner(props) {
|
|
|
66
87
|
Item,
|
|
67
88
|
{
|
|
68
89
|
text: t("Sign up"),
|
|
69
|
-
onClick: async () =>
|
|
90
|
+
onClick: async () => {
|
|
91
|
+
if (props.mockUser) {
|
|
92
|
+
console.log("Mock sign up - no navigation in demo mode");
|
|
93
|
+
} else {
|
|
94
|
+
await app.redirectToSignUp();
|
|
95
|
+
}
|
|
96
|
+
},
|
|
70
97
|
icon: /* @__PURE__ */ jsx(UserPlus, { ...iconProps })
|
|
71
98
|
}
|
|
72
99
|
),
|
|
@@ -83,7 +110,13 @@ function UserButtonInnerInner(props) {
|
|
|
83
110
|
Item,
|
|
84
111
|
{
|
|
85
112
|
text: t("Sign out"),
|
|
86
|
-
onClick: () =>
|
|
113
|
+
onClick: async () => {
|
|
114
|
+
if (props.mockUser) {
|
|
115
|
+
console.log("Mock sign out - no action taken in demo mode");
|
|
116
|
+
} else {
|
|
117
|
+
await user.signOut();
|
|
118
|
+
}
|
|
119
|
+
},
|
|
87
120
|
icon: /* @__PURE__ */ jsx(LogOut, { ...iconProps })
|
|
88
121
|
}
|
|
89
122
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/user-button.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Skeleton, Typography } from \"@stackframe/stack-ui\";\nimport { CircleUser, LogIn, LogOut, SunMoon, UserPlus } from \"lucide-react\";\nimport React, { Suspense } from \"react\";\nimport { CurrentUser, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { UserAvatar } from \"./elements/user-avatar\";\n\nfunction Item(props: { text: string, icon: React.ReactNode, onClick: () => void | Promise<void> }) {\n return (\n <DropdownMenuItem onClick={() => runAsynchronouslyWithAlert(props.onClick)}>\n <div className=\"flex gap-2 items-center\">\n {props.icon}\n <Typography>{props.text}</Typography>\n </div>\n </DropdownMenuItem>\n );\n}\n\ntype UserButtonProps = {\n showUserInfo?: boolean,\n colorModeToggle?: () => void | Promise<void>,\n extraItems?: {\n text: string,\n icon: React.ReactNode,\n onClick: () => void | Promise<void>,\n }[],\n};\n\nexport function UserButton(props: UserButtonProps) {\n return (\n <Suspense fallback={<Skeleton className=\"h-[34px] w-[34px] rounded-full stack-scope\" />}>\n <UserButtonInner {...props} />\n </Suspense>\n );\n}\n\nfunction UserButtonInner(props: UserButtonProps) {\n const
|
|
1
|
+
{"version":3,"sources":["../../../src/components/user-button.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Skeleton, Typography } from \"@stackframe/stack-ui\";\nimport { CircleUser, LogIn, LogOut, SunMoon, UserPlus } from \"lucide-react\";\nimport React, { Suspense } from \"react\";\nimport { CurrentUser, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { UserAvatar } from \"./elements/user-avatar\";\n\nfunction Item(props: { text: string, icon: React.ReactNode, onClick: () => void | Promise<void> }) {\n return (\n <DropdownMenuItem onClick={() => runAsynchronouslyWithAlert(props.onClick)}>\n <div className=\"flex gap-2 items-center\">\n {props.icon}\n <Typography>{props.text}</Typography>\n </div>\n </DropdownMenuItem>\n );\n}\n\ntype UserButtonProps = {\n showUserInfo?: boolean,\n colorModeToggle?: () => void | Promise<void>,\n extraItems?: {\n text: string,\n icon: React.ReactNode,\n onClick: () => void | Promise<void>,\n }[],\n mockUser?: {\n displayName?: string,\n primaryEmail?: string,\n profileImageUrl?: string,\n },\n};\n\nexport function UserButton(props: UserButtonProps) {\n return (\n <Suspense fallback={<Skeleton className=\"h-[34px] w-[34px] rounded-full stack-scope\" />}>\n <UserButtonInner {...props} />\n </Suspense>\n );\n}\n\nfunction UserButtonInner(props: UserButtonProps) {\n const userFromHook = useUser();\n\n // Use mock user if provided, otherwise use real user\n const user = props.mockUser ? {\n displayName: props.mockUser.displayName || 'Mock User',\n primaryEmail: props.mockUser.primaryEmail || 'mock@example.com',\n profileImageUrl: props.mockUser.profileImageUrl,\n signOut: () => {\n console.log('Mock sign out - no action taken in demo mode');\n return Promise.resolve();\n }\n } as CurrentUser : userFromHook;\n\n return <UserButtonInnerInner {...props} user={user} />;\n}\n\n\nfunction UserButtonInnerInner(props: UserButtonProps & { user: CurrentUser | null }) {\n const { t } = useTranslation();\n const user = props.user;\n const app = useStackApp();\n\n const iconProps = { size: 20, className: 'h-4 w-4' };\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger className=\"outline-none stack-scope\">\n <div className=\"flex gap-2 items-center\">\n <UserAvatar user={user} />\n {user && props.showUserInfo &&\n <div className=\"flex flex-col justify-center text-left\">\n <Typography className=\"max-w-40 truncate\">{user.displayName}</Typography>\n <Typography className=\"max-w-40 truncate\" variant=\"secondary\" type='label'>{user.primaryEmail}</Typography>\n </div>\n }\n </div>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"stack-scope\">\n <DropdownMenuLabel>\n <div className=\"flex gap-2 items-center\">\n <UserAvatar user={user} />\n <div>\n {user && <Typography className=\"max-w-40 truncate\">{user.displayName}</Typography>}\n {user && <Typography className=\"max-w-40 truncate\" variant=\"secondary\" type='label'>{user.primaryEmail}</Typography>}\n {!user && <Typography>{t('Not signed in')}</Typography>}\n </div>\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n {user && <Item\n text={t('Account settings')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock account settings - no navigation in demo mode');\n } else {\n await app.redirectToAccountSettings();\n }\n }}\n icon={<CircleUser {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign in')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign in - no navigation in demo mode');\n } else {\n await app.redirectToSignIn();\n }\n }}\n icon={<LogIn {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign up')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign up - no navigation in demo mode');\n } else {\n await app.redirectToSignUp();\n }\n }}\n icon={<UserPlus {...iconProps}/> }\n />}\n {user && props.extraItems && props.extraItems.map((item, index) => (\n <Item key={index} {...item} />\n ))}\n {props.colorModeToggle && (\n <Item\n text={t('Toggle theme')}\n onClick={props.colorModeToggle}\n icon={<SunMoon {...iconProps} />}\n />\n )}\n {user && <Item\n text={t('Sign out')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign out - no action taken in demo mode');\n } else {\n await user.signOut();\n }\n }}\n icon={<LogOut {...iconProps} />}\n />}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":"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","names":[]}
|