mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-21 01:41:31 +00:00
Checkpoint
This commit is contained in:
78
src/ConnectExplorer.tsx
Normal file
78
src/ConnectExplorer.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { useBoolean } from "@uifabric/react-hooks";
|
||||||
|
import { HttpHeaders } from "./Common/Constants";
|
||||||
|
import { GenerateTokenResponse } from "./Contracts/DataModels";
|
||||||
|
import { configContext } from "./ConfigContext";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
login: () => void;
|
||||||
|
setEncryptedToken: (token: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConnectExplorer: React.FunctionComponent<Props> = ({ setEncryptedToken, login }: Props) => {
|
||||||
|
const [connectionString, setConnectionString] = React.useState<string>("");
|
||||||
|
const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="connectExplorer" className="connectExplorerContainer" style={{ display: "flex" }}>
|
||||||
|
<div className="connectExplorerFormContainer">
|
||||||
|
<div className="connectExplorer">
|
||||||
|
<p className="connectExplorerContent">
|
||||||
|
<img src="images/HdeConnectCosmosDB.svg" alt="Azure Cosmos DB" />
|
||||||
|
</p>
|
||||||
|
<p className="welcomeText">Welcome to Azure Cosmos DB</p>
|
||||||
|
{isConnectionStringVisible ? (
|
||||||
|
<form
|
||||||
|
id="connectWithConnectionString"
|
||||||
|
onSubmit={async event => {
|
||||||
|
event.preventDefault();
|
||||||
|
const headers = new Headers();
|
||||||
|
headers.append(HttpHeaders.connectionString, connectionString);
|
||||||
|
const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken";
|
||||||
|
const response = await fetch(url, { headers, method: "POST" });
|
||||||
|
if (!response.ok) {
|
||||||
|
throw response;
|
||||||
|
}
|
||||||
|
// This API has a quirk where it must be parsed twice
|
||||||
|
const result: GenerateTokenResponse = JSON.parse(await response.json());
|
||||||
|
console.log(result.readWrite || result.read);
|
||||||
|
setEncryptedToken(decodeURIComponent(result.readWrite || result.read));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p className="connectExplorerContent connectStringText">Connect to your account with connection string</p>
|
||||||
|
<p className="connectExplorerContent">
|
||||||
|
<input
|
||||||
|
className="inputToken"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
placeholder="Please enter a connection string"
|
||||||
|
value={connectionString}
|
||||||
|
onChange={event => {
|
||||||
|
setConnectionString(event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="errorDetailsInfoTooltip" style={{ display: "none" }}>
|
||||||
|
<img className="errorImg" src="images/error.svg" alt="Error notification" />
|
||||||
|
<span className="errorDetails"></span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="connectExplorerContent">
|
||||||
|
<input className="filterbtnstyle" type="submit" value="Connect" />
|
||||||
|
</p>
|
||||||
|
<p className="switchConnectTypeText" onClick={login}>
|
||||||
|
Sign In with Azure Account
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
) : (
|
||||||
|
<div id="connectWithAad">
|
||||||
|
<input className="filterbtnstyle" type="button" value="Sign In" onClick={login} />
|
||||||
|
<p className="switchConnectTypeText" onClick={showConnectionString}>
|
||||||
|
Connect to your account with connection string
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
50
src/DirectoryPickerPanel.tsx
Normal file
50
src/DirectoryPickerPanel.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Panel, PanelType, ChoiceGroup } from "office-ui-fabric-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { useDirectories } from "./hooks/useDirectories";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
isOpen: boolean;
|
||||||
|
dismissPanel: () => void;
|
||||||
|
tenantId: string;
|
||||||
|
armToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DirectoryPickerPanel: React.FunctionComponent<Props> = ({
|
||||||
|
isOpen,
|
||||||
|
dismissPanel,
|
||||||
|
armToken,
|
||||||
|
tenantId
|
||||||
|
}: Props) => {
|
||||||
|
const directories = useDirectories(armToken);
|
||||||
|
return (
|
||||||
|
<Panel
|
||||||
|
type={PanelType.medium}
|
||||||
|
headerText="Select Directory"
|
||||||
|
isOpen={isOpen}
|
||||||
|
onDismiss={dismissPanel}
|
||||||
|
closeButtonAriaLabel="Close"
|
||||||
|
>
|
||||||
|
<ChoiceGroup
|
||||||
|
options={directories.map(dir => ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))}
|
||||||
|
selectedKey={tenantId}
|
||||||
|
onChange={async () => {
|
||||||
|
dismissPanel();
|
||||||
|
// TODO!!! This does not work. Still not sure why. Tried lots of stuff.
|
||||||
|
// const response = await msal.loginPopup({
|
||||||
|
// authority: `https://login.microsoftonline.com/${option.key}`
|
||||||
|
// });
|
||||||
|
// // msal = new Msal.UserAgentApplication({
|
||||||
|
// // auth: {
|
||||||
|
// // authority: `https://login.microsoftonline.com/${option.key}`,
|
||||||
|
// // clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
||||||
|
// // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development
|
||||||
|
// // }
|
||||||
|
// // });
|
||||||
|
// setTenantId(option.key);
|
||||||
|
// setAccount(response.account);
|
||||||
|
// console.log(account);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
};
|
||||||
22
src/FeedbackCommandButton.tsx
Normal file
22
src/FeedbackCommandButton.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import FeedbackIcon from "../images/Feedback.svg";
|
||||||
|
|
||||||
|
export const FeedbackCommandButton: React.FunctionComponent = () => {
|
||||||
|
return (
|
||||||
|
<div className="feedbackConnectSettingIcons">
|
||||||
|
<CommandButtonComponent
|
||||||
|
id="commandbutton-feedback"
|
||||||
|
iconSrc={FeedbackIcon}
|
||||||
|
iconAlt="feeback button"
|
||||||
|
onCommandClick={() =>
|
||||||
|
window.open("https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback")
|
||||||
|
}
|
||||||
|
ariaLabel="feeback button"
|
||||||
|
tooltipText="Send feedback"
|
||||||
|
hasPopup={true}
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,34 +1,22 @@
|
|||||||
import "./Platform/Hosted/ConnectScreen.less";
|
|
||||||
import { useBoolean } from "@uifabric/react-hooks";
|
import { useBoolean } from "@uifabric/react-hooks";
|
||||||
import {
|
import * as Msal from "msal";
|
||||||
DefaultButton,
|
import { initializeIcons } from "office-ui-fabric-react";
|
||||||
DirectionalHint,
|
|
||||||
FocusZone,
|
|
||||||
initializeIcons,
|
|
||||||
Panel,
|
|
||||||
PanelType,
|
|
||||||
Persona,
|
|
||||||
PersonaInitialsColor,
|
|
||||||
PersonaSize,
|
|
||||||
ChoiceGroup
|
|
||||||
} from "office-ui-fabric-react";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { render } from "react-dom";
|
import { render } from "react-dom";
|
||||||
import FeedbackIcon from "../images/Feedback.svg";
|
|
||||||
import ChevronRight from "../images/chevron-right.svg";
|
import ChevronRight from "../images/chevron-right.svg";
|
||||||
import "../less/hostedexplorer.less";
|
import "../less/hostedexplorer.less";
|
||||||
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
|
||||||
import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
|
||||||
import { useGraphPhoto } from "./hooks/useGraphPhoto";
|
|
||||||
import "./Shared/appInsights";
|
|
||||||
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
|
||||||
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
|
||||||
import { useDirectories } from "./hooks/useDirectories";
|
|
||||||
import * as Msal from "msal";
|
|
||||||
import { configContext } from "./ConfigContext";
|
|
||||||
import { HttpHeaders } from "./Common/Constants";
|
|
||||||
import { GenerateTokenResponse, DatabaseAccount } from "./Contracts/DataModels";
|
|
||||||
import { AuthType } from "./AuthType";
|
import { AuthType } from "./AuthType";
|
||||||
|
import { ConnectExplorer } from "./ConnectExplorer";
|
||||||
|
import { DatabaseAccount } from "./Contracts/DataModels";
|
||||||
|
import { DirectoryPickerPanel } from "./DirectoryPickerPanel";
|
||||||
|
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
||||||
|
import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
||||||
|
import { FeedbackCommandButton } from "./FeedbackCommandButton";
|
||||||
|
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
||||||
|
import { MeControl } from "./MeControl";
|
||||||
|
import "./Platform/Hosted/ConnectScreen.less";
|
||||||
|
import "./Shared/appInsights";
|
||||||
|
import { SignInButton } from "./SignInButton";
|
||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
@@ -58,9 +46,8 @@ const App: React.FunctionComponent = () => {
|
|||||||
const [encryptedToken, setEncryptedToken] = React.useState<string>(params && params.get("key"));
|
const [encryptedToken, setEncryptedToken] = React.useState<string>(params && params.get("key"));
|
||||||
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
|
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
|
||||||
|
|
||||||
// Hooks for showing/hiding UI
|
// Hooks for showing/hiding panel
|
||||||
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
||||||
const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
|
|
||||||
|
|
||||||
// Hooks for AAD authentication
|
// Hooks for AAD authentication
|
||||||
const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(
|
const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(
|
||||||
@@ -70,11 +57,8 @@ const App: React.FunctionComponent = () => {
|
|||||||
const [tenantId, setTenantId] = React.useState<string>(cachedTenantId);
|
const [tenantId, setTenantId] = React.useState<string>(cachedTenantId);
|
||||||
const [graphToken, setGraphToken] = React.useState<string>();
|
const [graphToken, setGraphToken] = React.useState<string>();
|
||||||
const [armToken, setArmToken] = React.useState<string>();
|
const [armToken, setArmToken] = React.useState<string>();
|
||||||
const [connectionString, setConnectionString] = React.useState<string>("");
|
|
||||||
const [databaseAccount, setDatabaseAccount] = React.useState<DatabaseAccount>();
|
const [databaseAccount, setDatabaseAccount] = React.useState<DatabaseAccount>();
|
||||||
|
|
||||||
const ref = React.useRef<HTMLIFrameElement>();
|
|
||||||
|
|
||||||
const login = React.useCallback(async () => {
|
const login = React.useCallback(async () => {
|
||||||
const response = await msal.loginPopup();
|
const response = await msal.loginPopup();
|
||||||
setLoggedIn();
|
setLoggedIn();
|
||||||
@@ -89,6 +73,8 @@ const App: React.FunctionComponent = () => {
|
|||||||
msal.logout();
|
msal.logout();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const ref = React.useRef<HTMLIFrameElement>();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (account && tenantId) {
|
if (account && tenantId) {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
@@ -120,11 +106,8 @@ const App: React.FunctionComponent = () => {
|
|||||||
}
|
}
|
||||||
}, [ref, encryptedToken, encryptedTokenMetadata, isLoggedIn, databaseAccount]);
|
}, [ref, encryptedToken, encryptedTokenMetadata, isLoggedIn, databaseAccount]);
|
||||||
|
|
||||||
const photo = useGraphPhoto(graphToken);
|
|
||||||
const directories = useDirectories(armToken);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<header>
|
<header>
|
||||||
<div className="items" role="menubar">
|
<div className="items" role="menubar">
|
||||||
<div className="cosmosDBTitle">
|
<div className="cosmosDBTitle">
|
||||||
@@ -151,77 +134,12 @@ const App: React.FunctionComponent = () => {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="feedbackConnectSettingIcons">
|
<FeedbackCommandButton />
|
||||||
<CommandButtonComponent
|
|
||||||
id="commandbutton-feedback"
|
|
||||||
iconSrc={FeedbackIcon}
|
|
||||||
iconAlt="feeback button"
|
|
||||||
onCommandClick={() =>
|
|
||||||
window.open("https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback")
|
|
||||||
}
|
|
||||||
ariaLabel="feeback button"
|
|
||||||
tooltipText="Send feedback"
|
|
||||||
hasPopup={true}
|
|
||||||
disabled={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="meControl">
|
<div className="meControl">
|
||||||
{isLoggedIn ? (
|
{isLoggedIn ? (
|
||||||
<FocusZone>
|
<MeControl {...{ graphToken, openPanel, logout, account }} />
|
||||||
<DefaultButton
|
|
||||||
id="mecontrolHeader"
|
|
||||||
className="mecontrolHeaderButton"
|
|
||||||
menuProps={{
|
|
||||||
className: "mecontrolContextualMenu",
|
|
||||||
isBeakVisible: false,
|
|
||||||
directionalHintFixed: true,
|
|
||||||
directionalHint: DirectionalHint.bottomRightEdge,
|
|
||||||
calloutProps: {
|
|
||||||
minPagePadding: 0
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
key: "SwitchDirectory",
|
|
||||||
text: "Switch Directory",
|
|
||||||
onClick: openPanel
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "SignOut",
|
|
||||||
text: "Sign Out",
|
|
||||||
onClick: logout
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
styles={{
|
|
||||||
rootHovered: { backgroundColor: "#393939" },
|
|
||||||
rootFocused: { backgroundColor: "#393939" },
|
|
||||||
rootPressed: { backgroundColor: "#393939" },
|
|
||||||
rootExpanded: { backgroundColor: "#393939" }
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Persona
|
|
||||||
imageUrl={photo}
|
|
||||||
text={account?.name}
|
|
||||||
secondaryText={account?.userName}
|
|
||||||
showSecondaryText={true}
|
|
||||||
showInitialsUntilImageLoads={true}
|
|
||||||
initialsColor={PersonaInitialsColor.teal}
|
|
||||||
size={PersonaSize.size28}
|
|
||||||
className="mecontrolHeaderPersona"
|
|
||||||
/>
|
|
||||||
</DefaultButton>
|
|
||||||
</FocusZone>
|
|
||||||
) : (
|
) : (
|
||||||
<DefaultButton
|
<SignInButton {...{ login }} />
|
||||||
className="mecontrolSigninButton"
|
|
||||||
text="Sign In"
|
|
||||||
onClick={login}
|
|
||||||
styles={{
|
|
||||||
rootHovered: { backgroundColor: "#393939", color: "#fff" },
|
|
||||||
rootFocused: { backgroundColor: "#393939", color: "#fff" },
|
|
||||||
rootPressed: { backgroundColor: "#393939", color: "#fff" }
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -242,102 +160,9 @@ const App: React.FunctionComponent = () => {
|
|||||||
src="explorer.html?v=1.0.1&platform=Hosted"
|
src="explorer.html?v=1.0.1&platform=Hosted"
|
||||||
></iframe>
|
></iframe>
|
||||||
)}
|
)}
|
||||||
{!isLoggedIn && !encryptedTokenMetadata && (
|
{!isLoggedIn && !encryptedTokenMetadata && <ConnectExplorer {...{ login, setEncryptedToken }} />}
|
||||||
<div id="connectExplorer" className="connectExplorerContainer" style={{ display: "flex" }}>
|
<DirectoryPickerPanel {...{ isOpen, dismissPanel, armToken, tenantId }} />
|
||||||
<div className="connectExplorerFormContainer">
|
</>
|
||||||
<div className="connectExplorer">
|
|
||||||
<p className="connectExplorerContent">
|
|
||||||
<img src="images/HdeConnectCosmosDB.svg" alt="Azure Cosmos DB" />
|
|
||||||
</p>
|
|
||||||
<p className="welcomeText">Welcome to Azure Cosmos DB</p>
|
|
||||||
{isConnectionStringVisible ? (
|
|
||||||
<form
|
|
||||||
id="connectWithConnectionString"
|
|
||||||
onSubmit={async event => {
|
|
||||||
event.preventDefault();
|
|
||||||
const headers = new Headers();
|
|
||||||
headers.append(HttpHeaders.connectionString, connectionString);
|
|
||||||
const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken";
|
|
||||||
const response = await fetch(url, { headers, method: "POST" });
|
|
||||||
if (!response.ok) {
|
|
||||||
throw response;
|
|
||||||
}
|
|
||||||
// This API has a quirk where it must be parsed twice
|
|
||||||
const result: GenerateTokenResponse = JSON.parse(await response.json());
|
|
||||||
console.log(result.readWrite || result.read);
|
|
||||||
setEncryptedToken(decodeURIComponent(result.readWrite || result.read));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p className="connectExplorerContent connectStringText">
|
|
||||||
Connect to your account with connection string
|
|
||||||
</p>
|
|
||||||
<p className="connectExplorerContent">
|
|
||||||
<input
|
|
||||||
className="inputToken"
|
|
||||||
type="text"
|
|
||||||
required
|
|
||||||
placeholder="Please enter a connection string"
|
|
||||||
value={connectionString}
|
|
||||||
onChange={event => {
|
|
||||||
setConnectionString(event.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="errorDetailsInfoTooltip" style={{ display: "none" }}>
|
|
||||||
<img className="errorImg" src="images/error.svg" alt="Error notification" />
|
|
||||||
<span className="errorDetails"></span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p className="connectExplorerContent">
|
|
||||||
<input className="filterbtnstyle" type="submit" value="Connect" />
|
|
||||||
</p>
|
|
||||||
<p className="switchConnectTypeText" onClick={login}>
|
|
||||||
Sign In with Azure Account
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
) : (
|
|
||||||
<div id="connectWithAad">
|
|
||||||
<input className="filterbtnstyle" type="button" value="Sign In" onClick={login} />
|
|
||||||
<p className="switchConnectTypeText" onClick={showConnectionString}>
|
|
||||||
Connect to your account with connection string
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div data-bind="react: firewallWarningComponentAdapter" />
|
|
||||||
<div data-bind="react: dialogComponentAdapter" />
|
|
||||||
<Panel
|
|
||||||
type={PanelType.medium}
|
|
||||||
headerText="Select Directory"
|
|
||||||
isOpen={isOpen}
|
|
||||||
onDismiss={dismissPanel}
|
|
||||||
closeButtonAriaLabel="Close"
|
|
||||||
>
|
|
||||||
<ChoiceGroup
|
|
||||||
options={directories.map(dir => ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))}
|
|
||||||
selectedKey={tenantId}
|
|
||||||
onChange={async () => {
|
|
||||||
dismissPanel();
|
|
||||||
// TODO!!! This does not work. Still not sure why. Tried lots of stuff.
|
|
||||||
// const response = await msal.loginPopup({
|
|
||||||
// authority: `https://login.microsoftonline.com/${option.key}`
|
|
||||||
// });
|
|
||||||
// // msal = new Msal.UserAgentApplication({
|
|
||||||
// // auth: {
|
|
||||||
// // authority: `https://login.microsoftonline.com/${option.key}`,
|
|
||||||
// // clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
|
||||||
// // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development
|
|
||||||
// // }
|
|
||||||
// // });
|
|
||||||
// setTenantId(option.key);
|
|
||||||
// setAccount(response.account);
|
|
||||||
// console.log(account);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Panel>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
68
src/MeControl.tsx
Normal file
68
src/MeControl.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import {
|
||||||
|
FocusZone,
|
||||||
|
DefaultButton,
|
||||||
|
DirectionalHint,
|
||||||
|
Persona,
|
||||||
|
PersonaInitialsColor,
|
||||||
|
PersonaSize
|
||||||
|
} from "office-ui-fabric-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { Account } from "msal";
|
||||||
|
import { useGraphPhoto } from "./hooks/useGraphPhoto";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
graphToken: string;
|
||||||
|
account: Account;
|
||||||
|
openPanel: () => void;
|
||||||
|
logout: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MeControl: React.FunctionComponent<Props> = ({ openPanel, logout, account, graphToken }: Props) => {
|
||||||
|
const photo = useGraphPhoto(graphToken);
|
||||||
|
return (
|
||||||
|
<FocusZone>
|
||||||
|
<DefaultButton
|
||||||
|
id="mecontrolHeader"
|
||||||
|
className="mecontrolHeaderButton"
|
||||||
|
menuProps={{
|
||||||
|
className: "mecontrolContextualMenu",
|
||||||
|
isBeakVisible: false,
|
||||||
|
directionalHintFixed: true,
|
||||||
|
directionalHint: DirectionalHint.bottomRightEdge,
|
||||||
|
calloutProps: {
|
||||||
|
minPagePadding: 0
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "SwitchDirectory",
|
||||||
|
text: "Switch Directory",
|
||||||
|
onClick: openPanel
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "SignOut",
|
||||||
|
text: "Sign Out",
|
||||||
|
onClick: logout
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
rootHovered: { backgroundColor: "#393939" },
|
||||||
|
rootFocused: { backgroundColor: "#393939" },
|
||||||
|
rootPressed: { backgroundColor: "#393939" },
|
||||||
|
rootExpanded: { backgroundColor: "#393939" }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Persona
|
||||||
|
imageUrl={photo}
|
||||||
|
text={account?.name}
|
||||||
|
secondaryText={account?.userName}
|
||||||
|
showSecondaryText={true}
|
||||||
|
showInitialsUntilImageLoads={true}
|
||||||
|
initialsColor={PersonaInitialsColor.teal}
|
||||||
|
size={PersonaSize.size28}
|
||||||
|
className="mecontrolHeaderPersona"
|
||||||
|
/>
|
||||||
|
</DefaultButton>
|
||||||
|
</FocusZone>
|
||||||
|
);
|
||||||
|
};
|
||||||
18
src/SignInButton.tsx
Normal file
18
src/SignInButton.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { DefaultButton } from "office-ui-fabric-react";
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
login: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SignInButton: React.FunctionComponent<Props> = ({ login }: Props) => {
|
||||||
|
return <DefaultButton
|
||||||
|
className="mecontrolSigninButton"
|
||||||
|
text="Sign In"
|
||||||
|
onClick={login}
|
||||||
|
styles={{
|
||||||
|
rootHovered: { backgroundColor: "#393939", color: "#fff" },
|
||||||
|
rootFocused: { backgroundColor: "#393939", color: "#fff" },
|
||||||
|
rootPressed: { backgroundColor: "#393939", color: "#fff" }
|
||||||
|
}} />;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user