Initial Fabric support (#1607)
* Add Platform.Fabric to run in context of Fabric
* Use separate StyleConstants
We want to have more flexibility with Styles at runtime
but Constants depend on ConfigContext and therefore
get loaded very early at startup.
* Add Fabric specific styles and Fluent theme
documentDBFabric.less contains all styles for Fabric.
We use React.lazy to import them conditionally at
runtime preventing webpack from preprocessing
them into main.css.
* Restyle CommandBar for Fabric
with more roundness and native colors.
* Disable Notebooks when running in Fabric
* Disable Synapse and Scripts commands for Fabric
* Fix code formatting issues
* Fetch encrypted token from sessionStorage for fabric platform
* Fix Tabs style
* Dark refresh icons for Fabric
* Use new ResourceTree2 for Fabric
* Fluent tree should have a fixed width
otherwise the action buttons jump around on hover.
* Disable remaining Script actions in Fabric
* Revert accidentally committed change
which broke a test
* Fix cross-origin error second try
* Adjust @FabrixBoxMargin css
* Hide Database Scale node on Fabric
* Remove all Collection child nodes on Fabric
* Add a comment about why we need FabricPlatform.tsx
* Fix equality checks
* Fix more Colors for Fabric
* Switch resource tree to "medium" size
* Fix formatting again
* Fix formatting again
* Disable no-var-requires error on some intended var import.
* Increase memory limit for build
* Use standard Segoe UI font for Fabric
* Improve Tabs design for Fabric
* Fix active Tab style bug in Portal
introduced with 39a7765aef
---------
Co-authored-by: Laurent Nguyen <laurent.nguyen@microsoft.com>
This commit is contained in:
parent
c2d2ff3dee
commit
379395567c
|
@ -85,6 +85,8 @@ jobs:
|
||||||
path: .cache
|
path: .cache
|
||||||
key: ${{ runner.os }}-build-cache
|
key: ${{ runner.os }}-build-cache
|
||||||
- run: npm run pack:prod
|
- run: npm run pack:prod
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: '--max-old-space-size=4096'
|
||||||
- run: cp -r ./Contracts ./dist/contracts
|
- run: cp -r ./Contracts ./dist/contracts
|
||||||
- run: cp -r ./configs ./dist/configs
|
- run: cp -r ./configs ./dist/configs
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
||||||
@SemiboldFont: "Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
@SemiboldFont: "Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
||||||
@GrayScale: "grayscale()";
|
@GrayScale: "grayscale()";
|
||||||
|
@NoColor: "brightness(0) saturate(100%)";
|
||||||
|
|
||||||
@xSmallFontSize: 4px;
|
@xSmallFontSize: 4px;
|
||||||
@smallFontSize: 8px;
|
@smallFontSize: 8px;
|
||||||
|
@ -147,14 +148,41 @@
|
||||||
// CommandBar
|
// CommandBar
|
||||||
@CommandBarButtonHeight: 40px;
|
@CommandBarButtonHeight: 40px;
|
||||||
|
|
||||||
|
/**********************************************************************************
|
||||||
|
Portal Consts
|
||||||
|
/**********************************************************************************/
|
||||||
|
|
||||||
|
@PortalAccentMediumHigh: #0058ad;
|
||||||
|
@PortalAccentMedium: #004e87;
|
||||||
|
@PortalAccentLight: #eef7ff;
|
||||||
|
@PortalAccentAccentExtra: #ddf0ff;
|
||||||
|
|
||||||
|
/**********************************************************************************
|
||||||
|
Fabric Consts
|
||||||
|
/**********************************************************************************/
|
||||||
|
|
||||||
|
@FabricFont: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
|
||||||
|
|
||||||
|
@FabricBoxBorderRadius: 8px;
|
||||||
|
@FabricBoxBorderShadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.14);
|
||||||
|
@FabricBoxMargin: 4px 3px 4px 3px;
|
||||||
|
|
||||||
|
@FabricAccentMediumHigh: #0c695a;
|
||||||
|
@FabricAccentMedium: #117865;
|
||||||
|
@FabricAccentLight: #f5f5f5;
|
||||||
|
@FabricAccentExtra: #ebebeb;
|
||||||
|
|
||||||
|
@FabricButtonBorderRadius: 4px;
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************************
|
/**********************************************************************************
|
||||||
Common Flex Property
|
Common Flex Property
|
||||||
/**********************************************************************************/
|
/**********************************************************************************/
|
||||||
|
|
||||||
.flex-display(@display: flex) {
|
.flex-display(@display: flex) {
|
||||||
display: ~"-webkit-@{display}";
|
display:~"-webkit-@{display}";
|
||||||
display: ~"-ms-@{display}box"; // IE10 uses -ms-flexbox
|
display:~"-ms-@{display}box"; // IE10 uses -ms-flexbox
|
||||||
display: ~"-ms-@{display}"; // IE11
|
display:~"-ms-@{display}"; // IE11
|
||||||
display: @display;
|
display: @display;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,13 +196,15 @@
|
||||||
High contrast mode active
|
High contrast mode active
|
||||||
**************************************************************************************/
|
**************************************************************************************/
|
||||||
|
|
||||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
@media all and (-ms-high-contrast: none),
|
||||||
|
(-ms-high-contrast: active) {
|
||||||
|
|
||||||
.selectedRadio,
|
.selectedRadio,
|
||||||
.selectedRadio:hover,
|
.selectedRadio:hover,
|
||||||
.selectedRadio:active,
|
.selectedRadio:active,
|
||||||
.selectedRadio.dirty,
|
.selectedRadio.dirty,
|
||||||
.tab [type="radio"]:checked ~ label,
|
.tab [type="radio"]:checked~label,
|
||||||
.tab [type="radio"]:checked ~ label:hover {
|
.tab [type="radio"]:checked~label:hover {
|
||||||
-ms-high-contrast-adjust: none;
|
-ms-high-contrast-adjust: none;
|
||||||
-webkit-text-fill-color: HighlightText;
|
-webkit-text-fill-color: HighlightText;
|
||||||
color: HighlightText;
|
color: HighlightText;
|
||||||
|
@ -183,6 +213,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryMetricsSummaryTuple {
|
.queryMetricsSummaryTuple {
|
||||||
|
|
||||||
th,
|
th,
|
||||||
td {
|
td {
|
||||||
&:nth-child(2) {
|
&:nth-child(2) {
|
||||||
|
@ -302,4 +333,4 @@
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-color: @InfoPointerColor transparent;
|
border-color: @InfoPointerColor transparent;
|
||||||
}
|
}
|
|
@ -2646,6 +2646,11 @@ a:link {
|
||||||
width: @ActiveTabWidth;
|
width: @ActiveTabWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .tabNavText {
|
||||||
|
font-weight: bolder;
|
||||||
|
border-bottom: 2px solid rgba(0,120,212,1);
|
||||||
|
}
|
||||||
|
|
||||||
.nav-tabs > li.active:focus > .tabNavContentContainer {
|
.nav-tabs > li.active:focus > .tabNavContentContainer {
|
||||||
.focus();
|
.focus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
@import "./Common/Constants";
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: @FabricFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: @FabricFont;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: @FabricAccentMedium;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
a:focus {
|
||||||
|
color: @FabricAccentMediumHigh;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#divExplorer {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resourceTreeAndTabs {
|
||||||
|
border-radius: @FabricBoxBorderRadius;
|
||||||
|
box-shadow: @FabricBoxBorderShadow;
|
||||||
|
margin: @FabricBoxMargin;
|
||||||
|
margin-top: 4px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabsManagerContainer {
|
||||||
|
background-color: #fafafa
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs-margin {
|
||||||
|
padding-top: 8px;
|
||||||
|
background-color: #fafafa
|
||||||
|
}
|
||||||
|
|
||||||
|
.commandBarContainer {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: @FabricBoxBorderRadius;
|
||||||
|
box-shadow: @FabricBoxBorderShadow;
|
||||||
|
margin: @FabricBoxMargin;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dividerContainer {
|
||||||
|
padding: @SmallSpace 0px @SmallSpace 0px;
|
||||||
|
.flex-display();
|
||||||
|
|
||||||
|
span {
|
||||||
|
border-left: @ButtonBorderWidth solid @BaseMedium;
|
||||||
|
margin: 0 10px 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.nav-tabs>li>.tabNavContentContainer>.tab_Content:hover {
|
||||||
|
border-bottom: 2px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs>li.active>.tabNavContentContainer>.tab_Content,
|
||||||
|
.nav-tabs>li.active>.tabNavContentContainer>.tab_Content:hover {
|
||||||
|
border-bottom: 2px solid @FabricAccentMedium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabNavContentContainer {
|
||||||
|
padding: @SmallSpace 0px @SmallSpace 0px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab_Content {
|
||||||
|
border-right: 0px none transparent;
|
||||||
|
margin: 0px @SmallSpace 0px @SmallSpace;
|
||||||
|
width: calc(@TabsWidth - (@SmallSpace * 2));
|
||||||
|
padding-bottom: @SmallSpace;
|
||||||
|
|
||||||
|
.statusIconContainer {
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabIconSection {
|
||||||
|
.cancelButton {
|
||||||
|
padding: 0px 0px 0px @SmallSpace;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.resourceTree {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion {
|
||||||
|
.accordionItemContainer {
|
||||||
|
.accordionItemHeader {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.treeComponent {
|
||||||
|
.nodeItem {
|
||||||
|
&:focus {
|
||||||
|
outline: 2px @FabricAccentMedium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.treeNodeHeader {
|
||||||
|
padding: 5px 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @FabricAccentLight;
|
||||||
|
|
||||||
|
.treeMenuEllipsis {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.showingMenu {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
&>.treeNodeHeader {
|
||||||
|
background-color: @FabricAccentExtra;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.dataExplorerErrorConsoleContainer {
|
||||||
|
border-radius: @FabricBoxBorderRadius;
|
||||||
|
box-shadow: @FabricBoxBorderShadow;
|
||||||
|
margin: @FabricBoxMargin;
|
||||||
|
width: auto;
|
||||||
|
align-self: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.filterbtnstyle {
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
border: solid 1px #d1d1d1;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterbtnstyle:hover {
|
||||||
|
background: @FabricAccentLight;
|
||||||
|
color: #000;
|
||||||
|
border: solid 1px #d1d1d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterbtnstyle:active {
|
||||||
|
background: @FabricAccentLight;
|
||||||
|
color: #000;
|
||||||
|
border: solid 1px #d1d1d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterbtnstyle:focus {
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
border: solid 1px #d1d1d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.gridRowSelected .tabdocumentsGridElement:hover {
|
||||||
|
background-color: @FabricAccentLight !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.refreshcol {
|
||||||
|
filter: brightness(0) saturate(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.refreshcol1 {
|
||||||
|
filter: brightness(0) saturate(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileImportImg img {
|
||||||
|
filter: brightness(0) saturate(100%);
|
||||||
|
}
|
|
@ -365,9 +365,6 @@ export const EmulatorMasterKey =
|
||||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||||
|
|
||||||
// A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable
|
|
||||||
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
|
|
||||||
|
|
||||||
export class Notebook {
|
export class Notebook {
|
||||||
public static readonly defaultBasePath = "./notebooks";
|
public static readonly defaultBasePath = "./notebooks";
|
||||||
public static readonly heartbeatDelayMs = 60000;
|
public static readonly heartbeatDelayMs = 60000;
|
||||||
|
|
|
@ -5,8 +5,10 @@ import refreshImg from "../../images/refresh-cosmos.svg";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import Explorer from "../Explorer/Explorer";
|
import Explorer from "../Explorer/Explorer";
|
||||||
import { ResourceTokenTree } from "../Explorer/Tree/ResourceTokenTree";
|
import { ResourceTokenTree } from "../Explorer/Tree/ResourceTokenTree";
|
||||||
|
import { ResourceTree2 } from "../Explorer/Tree2/ResourceTree";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
|
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
|
||||||
|
import { Platform, configContext } from "./../ConfigContext";
|
||||||
import { NormalizedEventKey } from "./Constants";
|
import { NormalizedEventKey } from "./Constants";
|
||||||
|
|
||||||
export interface ResourceTreeContainerProps {
|
export interface ResourceTreeContainerProps {
|
||||||
|
@ -76,10 +78,10 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
|
||||||
<ResourceTokenTree />
|
<ResourceTokenTree />
|
||||||
) : userContext.features.enableKoResourceTree ? (
|
) : userContext.features.enableKoResourceTree ? (
|
||||||
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
|
<div style={{ overflowY: "auto" }} data-bind="react:resourceTree" />
|
||||||
|
) : configContext.platform === Platform.Fabric ? (
|
||||||
|
<ResourceTree2 container={container} />
|
||||||
) : (
|
) : (
|
||||||
<ResourceTree container={container} />
|
<ResourceTree container={container} />
|
||||||
// Uncomment the following line to use the fluent ui tree
|
|
||||||
// <ResourceTree2 container={container} />
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Collections Window - End */}
|
{/* Collections Window - End */}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Platform, configContext } from "../ConfigContext";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
|
||||||
|
|
||||||
|
export function updateStyles(): void {
|
||||||
|
if (configContext.platform === Platform.Fabric) {
|
||||||
|
StyleConstants.AccentMediumHigh = StyleConstants.FabricAccentMediumHigh;
|
||||||
|
StyleConstants.AccentMedium = StyleConstants.FabricAccentMedium;
|
||||||
|
StyleConstants.AccentLight = StyleConstants.FabricAccentLight;
|
||||||
|
StyleConstants.AccentAccentExtra = StyleConstants.FabricAccentMediumHigh;
|
||||||
|
} else {
|
||||||
|
StyleConstants.AccentMediumHigh = StyleConstants.PortalAccentMediumHigh;
|
||||||
|
StyleConstants.AccentMedium = StyleConstants.PortalAccentMedium;
|
||||||
|
StyleConstants.AccentLight = StyleConstants.PortalAccentLight;
|
||||||
|
StyleConstants.AccentAccentExtra = StyleConstants.PortalAccentMediumHigh;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ export enum Platform {
|
||||||
Portal = "Portal",
|
Portal = "Portal",
|
||||||
Hosted = "Hosted",
|
Hosted = "Hosted",
|
||||||
Emulator = "Emulator",
|
Emulator = "Emulator",
|
||||||
|
Fabric = "Fabric",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigContext {
|
export interface ConfigContext {
|
||||||
|
@ -187,6 +188,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
||||||
console.error(`Invalid platform query parameter: ${platform}`);
|
console.error(`Invalid platform query parameter: ${platform}`);
|
||||||
break;
|
break;
|
||||||
case Platform.Portal:
|
case Platform.Portal:
|
||||||
|
case Platform.Fabric:
|
||||||
case Platform.Hosted:
|
case Platform.Hosted:
|
||||||
case Platform.Emulator:
|
case Platform.Emulator:
|
||||||
updateConfigContext({ platform });
|
updateConfigContext({ platform });
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import * as Plotly from "plotly.js-cartesian-dist-min";
|
import * as Plotly from "plotly.js-cartesian-dist-min";
|
||||||
import { StyleConstants } from "../../Common/Constants";
|
|
||||||
import { sendCachedDataMessage, sendReadyMessage } from "../../Common/MessageHandler";
|
import { sendCachedDataMessage, sendReadyMessage } from "../../Common/MessageHandler";
|
||||||
|
import { StyleConstants } from "../../Common/StyleConstants";
|
||||||
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
||||||
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
||||||
import "./Heatmap.less";
|
import "./Heatmap.less";
|
||||||
|
|
|
@ -18,6 +18,7 @@ import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
||||||
import { useSidePanel } from "../hooks/useSidePanel";
|
import { useSidePanel } from "../hooks/useSidePanel";
|
||||||
|
import { Platform, configContext } from "./../ConfigContext";
|
||||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||||
import Explorer from "./Explorer";
|
import Explorer from "./Explorer";
|
||||||
import { useNotebook } from "./Notebook/useNotebook";
|
import { useNotebook } from "./Notebook/useNotebook";
|
||||||
|
@ -99,7 +100,10 @@ export const createCollectionContextMenuButton = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
if (
|
||||||
|
configContext.platform !== Platform.Fabric &&
|
||||||
|
(userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||||
|
) {
|
||||||
items.push({
|
items.push({
|
||||||
iconSrc: AddStoredProcedureIcon,
|
iconSrc: AddStoredProcedureIcon,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
|
|
@ -23,9 +23,9 @@ import * as React from "react";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
|
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { StyleConstants } from "../../../Common/Constants";
|
|
||||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
import { QueriesClient } from "../../../Common/QueriesClient";
|
import { QueriesClient } from "../../../Common/QueriesClient";
|
||||||
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|
|
@ -23,7 +23,8 @@ import {
|
||||||
Text,
|
Text,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { StyleConstants, Urls } from "../../../Common/Constants";
|
import { Urls } from "../../../Common/Constants";
|
||||||
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
import { hoursInAMonth } from "../../../Shared/Constants";
|
import { hoursInAMonth } from "../../../Shared/Constants";
|
||||||
import {
|
import {
|
||||||
computeRUUsagePriceHourly,
|
computeRUUsagePriceHourly,
|
||||||
|
|
|
@ -18,6 +18,7 @@ import LoadingIndicator_3Squares from "../../../../images/LoadingIndicator_3Squa
|
||||||
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
|
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
|
||||||
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
|
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|
||||||
|
@ -237,7 +238,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
||||||
private renderContextMenuButton(node: TreeNode): JSX.Element {
|
private renderContextMenuButton(node: TreeNode): JSX.Element {
|
||||||
const menuItemLabel = "More";
|
const menuItemLabel = "More";
|
||||||
const buttonStyles: Partial<IButtonStyles> = {
|
const buttonStyles: Partial<IButtonStyles> = {
|
||||||
rootFocused: { outline: `1px dashed ${Constants.StyleConstants.FocusColor}` },
|
rootFocused: { outline: `1px dashed ${StyleConstants.FocusColor}` },
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Link } from "@fluentui/react/lib/Link";
|
import { Link } from "@fluentui/react/lib/Link";
|
||||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||||
import { sendMessage } from "Common/MessageHandler";
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
import { Platform } from "ConfigContext";
|
import { Platform, configContext } from "ConfigContext";
|
||||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
import { IGalleryItem } from "Juno/JunoClient";
|
import { IGalleryItem } from "Juno/JunoClient";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
|
||||||
|
@ -1343,9 +1343,10 @@ export default class Explorer {
|
||||||
|
|
||||||
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
||||||
const isNotebookEnabled =
|
const isNotebookEnabled =
|
||||||
userContext.features.notebooksDownBanner ||
|
configContext.platform !== Platform.Fabric &&
|
||||||
useNotebook.getState().isPhoenixNotebooks ||
|
(userContext.features.notebooksDownBanner ||
|
||||||
useNotebook.getState().isPhoenixFeatures;
|
useNotebook.getState().isPhoenixNotebooks ||
|
||||||
|
useNotebook.getState().isPhoenixFeatures);
|
||||||
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
|
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
|
||||||
useNotebook
|
useNotebook
|
||||||
.getState()
|
.getState()
|
||||||
|
|
|
@ -8,7 +8,9 @@ import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import create, { UseStore } from "zustand";
|
import create, { UseStore } from "zustand";
|
||||||
import { ConnectionStatusType, PoolIdType, StyleConstants } from "../../../Common/Constants";
|
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
||||||
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
|
import { Platform, configContext } from "../../../ConfigContext";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
|
@ -84,15 +86,27 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rootStyle =
|
||||||
|
configContext.platform === Platform.Fabric
|
||||||
|
? {
|
||||||
|
root: {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
padding: "0px 14px 0px 14px",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
root: {
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="commandBarContainer">
|
<div className="commandBarContainer">
|
||||||
<FluentCommandBar
|
<FluentCommandBar
|
||||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||||
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
||||||
farItems={uiFabricControlButtons}
|
farItems={uiFabricControlButtons}
|
||||||
styles={{
|
styles={rootStyle}
|
||||||
root: { backgroundColor: backgroundColor },
|
|
||||||
}}
|
|
||||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
overflowButtonProps={{ ariaLabel: "More commands" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -54,7 +54,11 @@ export function createStaticCommandBarButtons(
|
||||||
const buttons: CommandButtonComponentProps[] = [];
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
buttons.push(newCollectionBtn);
|
buttons.push(newCollectionBtn);
|
||||||
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
if (
|
||||||
|
configContext.platform !== Platform.Fabric &&
|
||||||
|
userContext.apiType !== "Tables" &&
|
||||||
|
userContext.apiType !== "Cassandra"
|
||||||
|
) {
|
||||||
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
||||||
|
|
||||||
if (addSynapseLink) {
|
if (addSynapseLink) {
|
||||||
|
@ -257,7 +261,9 @@ export function createDivider(): CommandButtonComponentProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function areScriptsSupported(): boolean {
|
function areScriptsSupported(): boolean {
|
||||||
return userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
return (
|
||||||
|
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
|
function createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
|
||||||
|
|
|
@ -9,7 +9,9 @@ import {
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
||||||
import { PoolIdType, StyleConstants } from "../../../Common/Constants";
|
import { PoolIdType } from "../../../Common/Constants";
|
||||||
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
|
import { configContext, Platform } from "../../../ConfigContext";
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
@ -24,11 +26,14 @@ import { MemoryTracker } from "./MemoryTrackerComponent";
|
||||||
export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => {
|
export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => {
|
||||||
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
|
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
|
||||||
|
|
||||||
|
const hoverColor =
|
||||||
|
configContext.platform == Platform.Fabric ? StyleConstants.FabricAccentLight : StyleConstants.AccentLight;
|
||||||
|
|
||||||
const getFilter = (isDisabled: boolean): string => {
|
const getFilter = (isDisabled: boolean): string => {
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
return StyleConstants.GrayScale;
|
return StyleConstants.GrayScale;
|
||||||
}
|
}
|
||||||
return undefined;
|
return configContext.platform == Platform.Fabric ? StyleConstants.NoColor : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
return btns
|
return btns
|
||||||
|
@ -68,6 +73,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||||
height: buttonHeightPx,
|
height: buttonHeightPx,
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
|
borderRadius: configContext.platform == Platform.Fabric ? StyleConstants.FabricButtonBorderRadius : "0px",
|
||||||
minWidth: 24,
|
minWidth: 24,
|
||||||
marginLeft: isSplit ? 0 : 5,
|
marginLeft: isSplit ? 0 : 5,
|
||||||
marginRight: isSplit ? 0 : 5,
|
marginRight: isSplit ? 0 : 5,
|
||||||
|
@ -79,17 +85,17 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||||
splitButtonMenuButton: {
|
splitButtonMenuButton: {
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
selectors: {
|
selectors: {
|
||||||
":hover": { backgroundColor: StyleConstants.AccentLight },
|
":hover": { backgroundColor: hoverColor },
|
||||||
},
|
},
|
||||||
width: 16,
|
width: 16,
|
||||||
},
|
},
|
||||||
label: { fontSize: StyleConstants.mediumFontSize },
|
label: { fontSize: StyleConstants.mediumFontSize },
|
||||||
rootHovered: { backgroundColor: StyleConstants.AccentLight },
|
rootHovered: { backgroundColor: hoverColor },
|
||||||
rootPressed: { backgroundColor: StyleConstants.AccentLight },
|
rootPressed: { backgroundColor: hoverColor },
|
||||||
splitButtonMenuButtonExpanded: {
|
splitButtonMenuButtonExpanded: {
|
||||||
backgroundColor: StyleConstants.AccentExtra,
|
backgroundColor: StyleConstants.AccentExtra,
|
||||||
selectors: {
|
selectors: {
|
||||||
":hover": { backgroundColor: StyleConstants.AccentLight },
|
":hover": { backgroundColor: hoverColor },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
splitButtonDivider: {
|
splitButtonDivider: {
|
||||||
|
@ -120,7 +126,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
|
||||||
// TODO Remove all this crazy styling once we adopt Ui-Fabric Azure themes
|
// TODO Remove all this crazy styling once we adopt Ui-Fabric Azure themes
|
||||||
selectors: {
|
selectors: {
|
||||||
".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
|
".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
|
||||||
".ms-ContextualMenu-link:hover": { backgroundColor: StyleConstants.AccentLight },
|
".ms-ContextualMenu-link:hover": { backgroundColor: hoverColor },
|
||||||
".ms-ContextualMenu-icon": { width: 16, height: 16 },
|
".ms-ContextualMenu-icon": { width: 16, height: 16 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@ import distanceInWordsToNow from "date-fns/distance_in_words_to_now";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { StyleConstants } from "../../../Common/Constants";
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
lastSaved?: Date | null;
|
lastSaved?: Date | null;
|
||||||
|
|
|
@ -11,8 +11,9 @@ import {
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { QueryCopilotSampleDatabaseId, StyleConstants } from "Common/Constants";
|
import { QueryCopilotSampleDatabaseId } from "Common/Constants";
|
||||||
import { handleError } from "Common/ErrorHandlingUtils";
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
|
import { StyleConstants } from "Common/StyleConstants";
|
||||||
import { createCollection } from "Common/dataAccess/createCollection";
|
import { createCollection } from "Common/dataAccess/createCollection";
|
||||||
import * as DataModels from "Contracts/DataModels";
|
import * as DataModels from "Contracts/DataModels";
|
||||||
import { ContainerSampleGenerator } from "Explorer/DataSamples/ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "Explorer/DataSamples/ContainerSampleGenerator";
|
||||||
|
|
|
@ -138,12 +138,7 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span className="tabNavText">{useObservable(tab?.tabTitle || getReactTabTitle())}</span>
|
||||||
className="tabNavText"
|
|
||||||
style={active ? { fontWeight: "bolder", borderBottom: "2px solid rgba(0,120,212,1)" } : {}}
|
|
||||||
>
|
|
||||||
{useObservable(tab?.tabTitle || getReactTabTitle())}
|
|
||||||
</span>
|
|
||||||
{tabKind !== ReactTabKind.Home && (
|
{tabKind !== ReactTabKind.Home && (
|
||||||
<span className="tabIconSection">
|
<span className="tabIconSection">
|
||||||
<CloseButton tab={tab} active={active} hovering={hovering} tabKind={tabKind} />
|
<CloseButton tab={tab} active={active} hovering={hovering} tabKind={tabKind} />
|
||||||
|
|
|
@ -37,6 +37,7 @@ import QueryTablesTab from "../Tabs/QueryTablesTab";
|
||||||
import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2";
|
import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2";
|
||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
import { Platform, configContext } from "./../../ConfigContext";
|
||||||
import ConflictId from "./ConflictId";
|
import ConflictId from "./ConflictId";
|
||||||
import DocumentId from "./DocumentId";
|
import DocumentId from "./DocumentId";
|
||||||
import StoredProcedure from "./StoredProcedure";
|
import StoredProcedure from "./StoredProcedure";
|
||||||
|
@ -205,7 +206,8 @@ export default class Collection implements ViewModels.Collection {
|
||||||
.map((node) => <Trigger>node);
|
.map((node) => <Trigger>node);
|
||||||
});
|
});
|
||||||
|
|
||||||
const showScriptsMenus: boolean = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
const showScriptsMenus: boolean =
|
||||||
|
configContext.platform != Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||||
this.showStoredProcedures = ko.observable<boolean>(showScriptsMenus);
|
this.showStoredProcedures = ko.observable<boolean>(showScriptsMenus);
|
||||||
this.showTriggers = ko.observable<boolean>(showScriptsMenus);
|
this.showTriggers = ko.observable<boolean>(showScriptsMenus);
|
||||||
this.showUserDefinedFunctions = ko.observable<boolean>(showScriptsMenus);
|
this.showUserDefinedFunctions = ko.observable<boolean>(showScriptsMenus);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
import { Platform, configContext } from "./../../ConfigContext";
|
||||||
import StoredProcedure from "./StoredProcedure";
|
import StoredProcedure from "./StoredProcedure";
|
||||||
import Trigger from "./Trigger";
|
import Trigger from "./Trigger";
|
||||||
import UserDefinedFunction from "./UserDefinedFunction";
|
import UserDefinedFunction from "./UserDefinedFunction";
|
||||||
|
@ -69,7 +70,8 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
||||||
shallow
|
shallow
|
||||||
);
|
);
|
||||||
const { activeTab, refreshActiveTab } = useTabs();
|
const { activeTab, refreshActiveTab } = useTabs();
|
||||||
const showScriptNodes = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
const showScriptNodes =
|
||||||
|
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||||
const pseudoDirPath = "PsuedoDir";
|
const pseudoDirPath = "PsuedoDir";
|
||||||
|
|
||||||
const buildGalleryCallout = (): JSX.Element => {
|
const buildGalleryCallout = (): JSX.Element => {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
import { Platform, configContext } from "./../../ConfigContext";
|
||||||
import StoredProcedure from "./StoredProcedure";
|
import StoredProcedure from "./StoredProcedure";
|
||||||
import Trigger from "./Trigger";
|
import Trigger from "./Trigger";
|
||||||
import UserDefinedFunction from "./UserDefinedFunction";
|
import UserDefinedFunction from "./UserDefinedFunction";
|
||||||
|
@ -249,7 +250,9 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||||
* @param container
|
* @param container
|
||||||
*/
|
*/
|
||||||
private static showScriptNodes(container: Explorer): boolean {
|
private static showScriptNodes(container: Explorer): boolean {
|
||||||
return userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
return (
|
||||||
|
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildCollectionNode(database: ViewModels.Database, collection: ViewModels.Collection): TreeNode {
|
private buildCollectionNode(database: ViewModels.Database, collection: ViewModels.Collection): TreeNode {
|
||||||
|
|
|
@ -89,8 +89,8 @@ export const ResourceTree2: React.FC<ResourceTreeProps> = ({ container }: Resour
|
||||||
aria-label="CosmosDB resources"
|
aria-label="CosmosDB resources"
|
||||||
openItems={openItems}
|
openItems={openItems}
|
||||||
onOpenChange={handleOpenChange}
|
onOpenChange={handleOpenChange}
|
||||||
size="small"
|
size="medium"
|
||||||
style={{ height: "100%" }}
|
style={{ height: "100%", width: "290px" }}
|
||||||
>
|
>
|
||||||
{[dataNodeTree].map((node) => (
|
{[dataNodeTree].map((node) => (
|
||||||
<TreeNode2Component
|
<TreeNode2Component
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
||||||
import { useNotebook } from "../Notebook/useNotebook";
|
import { useNotebook } from "../Notebook/useNotebook";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
import { Platform, configContext } from "./../../ConfigContext";
|
||||||
|
|
||||||
export const buildCollectionNode = (
|
export const buildCollectionNode = (
|
||||||
database: ViewModels.Database,
|
database: ViewModels.Database,
|
||||||
|
@ -25,6 +26,46 @@ export const buildCollectionNode = (
|
||||||
container: Explorer,
|
container: Explorer,
|
||||||
refreshActiveTab: (comparator: (tab: TabsBase) => boolean) => void
|
refreshActiveTab: (comparator: (tab: TabsBase) => boolean) => void
|
||||||
): TreeNode2 => {
|
): TreeNode2 => {
|
||||||
|
let children: TreeNode2[];
|
||||||
|
|
||||||
|
// Flat Tree for Fabric
|
||||||
|
if (configContext.platform !== Platform.Fabric) {
|
||||||
|
children = buildCollectionNodeChildren(database, collection, isNotebookEnabled, container, refreshActiveTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: collection.id(),
|
||||||
|
iconSrc: CollectionIcon,
|
||||||
|
children: children,
|
||||||
|
className: "collectionHeader",
|
||||||
|
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(container, collection),
|
||||||
|
onClick: () => {
|
||||||
|
useSelectedNode.getState().setSelectedNode(collection);
|
||||||
|
collection.openTab();
|
||||||
|
// push to most recent
|
||||||
|
mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
|
||||||
|
},
|
||||||
|
onExpanded: () => {
|
||||||
|
// Rewritten version of expandCollapseCollection
|
||||||
|
useSelectedNode.getState().setSelectedNode(collection);
|
||||||
|
useCommandBar.getState().setContextButtons([]);
|
||||||
|
refreshActiveTab(
|
||||||
|
(tab: TabsBase) =>
|
||||||
|
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isSelected: () => useSelectedNode.getState().isDataNodeSelected(collection.databaseId, collection.id()),
|
||||||
|
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(collection),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildCollectionNodeChildren = (
|
||||||
|
database: ViewModels.Database,
|
||||||
|
collection: ViewModels.Collection,
|
||||||
|
isNotebookEnabled: boolean,
|
||||||
|
container: Explorer,
|
||||||
|
refreshActiveTab: (comparator: (tab: TabsBase) => boolean) => void
|
||||||
|
): TreeNode2[] => {
|
||||||
const showScriptNodes = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
const showScriptNodes = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||||
const children: TreeNode2[] = [];
|
const children: TreeNode2[] = [];
|
||||||
children.push({
|
children.push({
|
||||||
|
@ -110,27 +151,7 @@ export const buildCollectionNode = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return children;
|
||||||
label: collection.id(),
|
|
||||||
iconSrc: CollectionIcon,
|
|
||||||
children: children,
|
|
||||||
className: "collectionHeader",
|
|
||||||
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(container, collection),
|
|
||||||
onClick: () => {
|
|
||||||
useSelectedNode.getState().setSelectedNode(collection);
|
|
||||||
},
|
|
||||||
onExpanded: () => {
|
|
||||||
// Rewritten version of expandCollapseCollection
|
|
||||||
useSelectedNode.getState().setSelectedNode(collection);
|
|
||||||
useCommandBar.getState().setContextButtons([]);
|
|
||||||
refreshActiveTab(
|
|
||||||
(tab: TabsBase) =>
|
|
||||||
tab.collection?.id() === collection.id() && tab.collection.databaseId === collection.databaseId
|
|
||||||
);
|
|
||||||
},
|
|
||||||
isSelected: () => useSelectedNode.getState().isDataNodeSelected(collection.databaseId, collection.id()),
|
|
||||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(collection),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildStoredProcedureNode = (
|
const buildStoredProcedureNode = (
|
||||||
|
|
|
@ -9,6 +9,7 @@ import * as ResourceTreeContextMenuButtonFactory from "../ContextMenuButtonFacto
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
import { Platform, configContext } from "./../../ConfigContext";
|
||||||
|
|
||||||
export const useDatabaseTreeNodes = (container: Explorer, isNotebookEnabled: boolean): TreeNode2[] => {
|
export const useDatabaseTreeNodes = (container: Explorer, isNotebookEnabled: boolean): TreeNode2[] => {
|
||||||
const databases = useDatabases((state) => state.databases);
|
const databases = useDatabases((state) => state.databases);
|
||||||
|
@ -35,7 +36,7 @@ export const useDatabaseTreeNodes = (container: Explorer, isNotebookEnabled: boo
|
||||||
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(database),
|
onContextMenuOpen: () => useSelectedNode.getState().setSelectedNode(database),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (database.isDatabaseShared()) {
|
if (database.isDatabaseShared() && configContext.platform !== Platform.Fabric) {
|
||||||
databaseNode.children.push({
|
databaseNode.children.push({
|
||||||
id: database.isSampleDB ? "sampleScaleSettings" : "",
|
id: database.isSampleDB ? "sampleScaleSettings" : "",
|
||||||
label: "Scale",
|
label: "Scale",
|
||||||
|
|
23
src/Main.tsx
23
src/Main.tsx
|
@ -1,5 +1,5 @@
|
||||||
// CSS Dependencies
|
// CSS Dependencies
|
||||||
import { initializeIcons } from "@fluentui/react";
|
import { initializeIcons, loadTheme } from "@fluentui/react";
|
||||||
import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel";
|
import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel";
|
||||||
import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial";
|
import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial";
|
||||||
import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial";
|
import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial";
|
||||||
|
@ -26,6 +26,8 @@ import "../less/TableStyles/CustomizeColumns.less";
|
||||||
import "../less/TableStyles/EntityEditor.less";
|
import "../less/TableStyles/EntityEditor.less";
|
||||||
import "../less/TableStyles/fulldatatables.less";
|
import "../less/TableStyles/fulldatatables.less";
|
||||||
import "../less/TableStyles/queryBuilder.less";
|
import "../less/TableStyles/queryBuilder.less";
|
||||||
|
import * as StyleConstants from "./Common/StyleConstants";
|
||||||
|
import { configContext, Platform } from "ConfigContext";
|
||||||
import "../less/documentDB.less";
|
import "../less/documentDB.less";
|
||||||
import "../less/forms.less";
|
import "../less/forms.less";
|
||||||
import "../less/infobox.less";
|
import "../less/infobox.less";
|
||||||
|
@ -57,6 +59,7 @@ import "./Libs/jquery";
|
||||||
import "./Shared/appInsights";
|
import "./Shared/appInsights";
|
||||||
import { useConfig } from "./hooks/useConfig";
|
import { useConfig } from "./hooks/useConfig";
|
||||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||||
|
import { appThemeFabric } from "./Platform/Fabric/FabricTheme";
|
||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
|
@ -67,6 +70,10 @@ const App: React.FunctionComponent = () => {
|
||||||
const shouldShowModal = useQueryCopilot((state) => state.showFeedbackModal);
|
const shouldShowModal = useQueryCopilot((state) => state.showFeedbackModal);
|
||||||
|
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
|
if (config?.platform === Platform.Fabric) {
|
||||||
|
loadTheme(appThemeFabric);
|
||||||
|
}
|
||||||
|
StyleConstants.updateStyles();
|
||||||
const explorer = useKnockoutExplorer(config?.platform);
|
const explorer = useKnockoutExplorer(config?.platform);
|
||||||
|
|
||||||
const toggleLeftPaneExpanded = () => {
|
const toggleLeftPaneExpanded = () => {
|
||||||
|
@ -84,6 +91,7 @@ const App: React.FunctionComponent = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flexContainer" aria-hidden="false">
|
<div className="flexContainer" aria-hidden="false">
|
||||||
|
<LoadFabricOverrides />
|
||||||
<div id="divExplorer" className="flexContainer hideOverflows">
|
<div id="divExplorer" className="flexContainer hideOverflows">
|
||||||
<div id="freeTierTeachingBubble"> </div>
|
<div id="freeTierTeachingBubble"> </div>
|
||||||
{/* Main Command Bar - Start */}
|
{/* Main Command Bar - Start */}
|
||||||
|
@ -135,6 +143,19 @@ const App: React.FunctionComponent = () => {
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.body);
|
ReactDOM.render(<App />, document.body);
|
||||||
|
|
||||||
|
function LoadFabricOverrides(): JSX.Element {
|
||||||
|
if (configContext.platform === Platform.Fabric) {
|
||||||
|
const FabricStyle = React.lazy(() => import("./Platform/Fabric/FabricPlatform"));
|
||||||
|
return (
|
||||||
|
<React.Suspense fallback={<div></div>}>
|
||||||
|
<FabricStyle />
|
||||||
|
</React.Suspense>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function LoadingExplorer(): JSX.Element {
|
function LoadingExplorer(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="splashLoaderContainer">
|
<div className="splashLoaderContainer">
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import React from "react";
|
||||||
|
import "../../../less/documentDBFabric.less";
|
||||||
|
// This is a dummy export, allowing us to conditionally import documentDBFabric.less
|
||||||
|
// by lazy-importing this in Main.tsx (see LoadFabricOverrides() there)
|
||||||
|
export default function InitFabric() {
|
||||||
|
return <></>;
|
||||||
|
}
|
|
@ -0,0 +1,208 @@
|
||||||
|
import { Theme, createTheme } from "@fluentui/react";
|
||||||
|
|
||||||
|
export const appThemeFabric: Theme = createTheme({
|
||||||
|
palette: {
|
||||||
|
/**
|
||||||
|
* Color code for themeDarker.
|
||||||
|
*/
|
||||||
|
themeDarker: "#033f38",
|
||||||
|
/**
|
||||||
|
* Color code for themeDark.
|
||||||
|
*/
|
||||||
|
themeDark: "#0a5c50",
|
||||||
|
/**
|
||||||
|
* Color code for themeDarkAlt.
|
||||||
|
*/
|
||||||
|
themeDarkAlt: "#0c695a",
|
||||||
|
/**
|
||||||
|
* Color code for themePrimary.
|
||||||
|
*/
|
||||||
|
themePrimary: "#117865",
|
||||||
|
/**
|
||||||
|
* Color code for themeSecondary.
|
||||||
|
*/
|
||||||
|
themeSecondary: "#1f937e",
|
||||||
|
/**
|
||||||
|
* Color code for themeTertiary.
|
||||||
|
*/
|
||||||
|
themeTertiary: "#52c7aa",
|
||||||
|
/**
|
||||||
|
* Color code for themeLight.
|
||||||
|
*/
|
||||||
|
themeLight: "#9ee0cb",
|
||||||
|
/**
|
||||||
|
* Color code for themeLighter.
|
||||||
|
*/
|
||||||
|
themeLighter: "#c0ecdd",
|
||||||
|
/**
|
||||||
|
* Color code for themeLighterAlt.
|
||||||
|
*/
|
||||||
|
themeLighterAlt: "#e3f7ef",
|
||||||
|
/**
|
||||||
|
* Color code for the strongest color, which is black in the default theme.
|
||||||
|
* This is a very light color in inverted themes.
|
||||||
|
*/
|
||||||
|
black: "#000000",
|
||||||
|
/**
|
||||||
|
* Color code for blackTranslucent40.
|
||||||
|
*/
|
||||||
|
blackTranslucent40: "rgba(0, 0, 0, 0.4)",
|
||||||
|
/**
|
||||||
|
* Color code for neutralDark.
|
||||||
|
*/
|
||||||
|
neutralDark: "#141414",
|
||||||
|
/**
|
||||||
|
* Color code for neutralPrimary.
|
||||||
|
*/
|
||||||
|
neutralPrimary: "#242424",
|
||||||
|
/**
|
||||||
|
* Color code for neutralPrimaryAlt.
|
||||||
|
*/
|
||||||
|
neutralPrimaryAlt: "#383838",
|
||||||
|
/**
|
||||||
|
* Color code for neutralSecondary.
|
||||||
|
*/
|
||||||
|
neutralSecondary: "#5c5c5c",
|
||||||
|
/**
|
||||||
|
* Color code for neutralSecondaryAlt.
|
||||||
|
*/
|
||||||
|
neutralSecondaryAlt: "#858585",
|
||||||
|
/**
|
||||||
|
* Color code for neutralTertiary.
|
||||||
|
*/
|
||||||
|
neutralTertiary: "#9e9e9e",
|
||||||
|
/**
|
||||||
|
* Color code for neutralTertiaryAlt.
|
||||||
|
*/
|
||||||
|
neutralTertiaryAlt: "#c7c7c7",
|
||||||
|
/**
|
||||||
|
* Color code for neutralQuaternary.
|
||||||
|
*/
|
||||||
|
neutralQuaternary: "#d1d1d1",
|
||||||
|
/**
|
||||||
|
* Color code for neutralQuaternaryAlt.
|
||||||
|
*/
|
||||||
|
neutralQuaternaryAlt: "#e0e0e0",
|
||||||
|
/**
|
||||||
|
* Color code for neutralLight.
|
||||||
|
*/
|
||||||
|
neutralLight: "#ebebeb",
|
||||||
|
/**
|
||||||
|
* Color code for neutralLighter.
|
||||||
|
*/
|
||||||
|
neutralLighter: "#f5f5f5",
|
||||||
|
/**
|
||||||
|
* Color code for neutralLighterAlt.
|
||||||
|
*/
|
||||||
|
neutralLighterAlt: "#fafafa",
|
||||||
|
/**
|
||||||
|
* Color code for the accent.
|
||||||
|
*/
|
||||||
|
accent: "#117865",
|
||||||
|
/**
|
||||||
|
* Color code for the softest color, which is white in the default theme. This is a very dark color in dark themes.
|
||||||
|
* This is the page background.
|
||||||
|
*/
|
||||||
|
white: "#ffffff",
|
||||||
|
/**
|
||||||
|
* Color code for whiteTranslucent40
|
||||||
|
*/
|
||||||
|
whiteTranslucent40: "rgba(255, 255, 255, 0.4)",
|
||||||
|
/**
|
||||||
|
* Color code for yellowDark.
|
||||||
|
*/
|
||||||
|
yellowDark: "#d39300",
|
||||||
|
/**
|
||||||
|
* Color code for yellow.
|
||||||
|
*/
|
||||||
|
yellow: "#fde300",
|
||||||
|
/**
|
||||||
|
* Color code for yellowLight.
|
||||||
|
*/
|
||||||
|
yellowLight: "#fef7b2",
|
||||||
|
/**
|
||||||
|
* Color code for orange.
|
||||||
|
*/
|
||||||
|
orange: "#f7630c",
|
||||||
|
/**
|
||||||
|
* Color code for orangeLight.
|
||||||
|
*/
|
||||||
|
orangeLight: "#f98845",
|
||||||
|
/**
|
||||||
|
* Color code for orangeLighter.
|
||||||
|
*/
|
||||||
|
orangeLighter: "#fdcfb4",
|
||||||
|
/**
|
||||||
|
* Color code for redDark.
|
||||||
|
*/
|
||||||
|
redDark: "#750b1c",
|
||||||
|
/**
|
||||||
|
* Color code for red.
|
||||||
|
*/
|
||||||
|
red: "#d13438",
|
||||||
|
/**
|
||||||
|
* Color code for magentaDark.
|
||||||
|
*/
|
||||||
|
magentaDark: "#6b0043",
|
||||||
|
/**
|
||||||
|
* Color code for magenta.
|
||||||
|
*/
|
||||||
|
magenta: "#bf0077",
|
||||||
|
/**
|
||||||
|
* Color code for magentaLight.
|
||||||
|
*/
|
||||||
|
magentaLight: "#d957a8",
|
||||||
|
/**
|
||||||
|
* Color code for purpleDark.
|
||||||
|
*/
|
||||||
|
purpleDark: "#401b6c",
|
||||||
|
/**
|
||||||
|
* Color code for purple.
|
||||||
|
*/
|
||||||
|
purple: "#5c2e91",
|
||||||
|
/**
|
||||||
|
* Color code for purpleLight.
|
||||||
|
*/
|
||||||
|
purpleLight: "#c6b1de",
|
||||||
|
/**
|
||||||
|
* Color code for blueDark.
|
||||||
|
*/
|
||||||
|
blueDark: "#003966",
|
||||||
|
/**
|
||||||
|
* Color code for blueMid.
|
||||||
|
*/
|
||||||
|
blueMid: "#004e8c",
|
||||||
|
/**
|
||||||
|
* Color code for blue.
|
||||||
|
*/
|
||||||
|
blue: "#0078d4",
|
||||||
|
/**
|
||||||
|
* Color code for blueLight.
|
||||||
|
*/
|
||||||
|
blueLight: "#3a96dd",
|
||||||
|
/**
|
||||||
|
* Color code for tealDark.
|
||||||
|
*/
|
||||||
|
tealDark: "#006666",
|
||||||
|
/**
|
||||||
|
* Color code for teal.
|
||||||
|
*/
|
||||||
|
teal: "#038387",
|
||||||
|
/**
|
||||||
|
* Color code for tealLight.
|
||||||
|
*/
|
||||||
|
tealLight: "#00b7c3",
|
||||||
|
/**
|
||||||
|
* Color code for greenDark.
|
||||||
|
*/
|
||||||
|
greenDark: "#0b6a0b",
|
||||||
|
/**
|
||||||
|
* Color code for green.
|
||||||
|
*/
|
||||||
|
green: "#107c10",
|
||||||
|
/**
|
||||||
|
* Color code for greenLight.
|
||||||
|
*/
|
||||||
|
greenLight: "#13a10e",
|
||||||
|
},
|
||||||
|
});
|
|
@ -4,7 +4,7 @@
|
||||||
import { DefaultButton, IButtonStyles, IContextualMenuItem } from "@fluentui/react";
|
import { DefaultButton, IButtonStyles, IContextualMenuItem } from "@fluentui/react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { FunctionComponent, useEffect, useState } from "react";
|
import { FunctionComponent, useEffect, useState } from "react";
|
||||||
import { StyleConstants } from "../../../Common/Constants";
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
||||||
import { useDatabaseAccounts } from "../../../hooks/useDatabaseAccounts";
|
import { useDatabaseAccounts } from "../../../hooks/useDatabaseAccounts";
|
||||||
import { useSubscriptions } from "../../../hooks/useSubscriptions";
|
import { useSubscriptions } from "../../../hooks/useSubscriptions";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useBoolean } from "@fluentui/react-hooks";
|
import { useBoolean } from "@fluentui/react-hooks";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ErrorImage from "../../../../images/error.svg";
|
|
||||||
import ConnectImage from "../../../../images/HdeConnectCosmosDB.svg";
|
import ConnectImage from "../../../../images/HdeConnectCosmosDB.svg";
|
||||||
|
import ErrorImage from "../../../../images/error.svg";
|
||||||
import { AuthType } from "../../../AuthType";
|
import { AuthType } from "../../../AuthType";
|
||||||
import { HttpHeaders } from "../../../Common/Constants";
|
import { HttpHeaders } from "../../../Common/Constants";
|
||||||
import { configContext } from "../../../ConfigContext";
|
import { configContext } from "../../../ConfigContext";
|
||||||
|
@ -16,6 +16,19 @@ interface Props {
|
||||||
setAuthType: (authType: AuthType) => void;
|
setAuthType: (authType: AuthType) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fetchEncryptedToken = async (connectionString: string): Promise<string> => {
|
||||||
|
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());
|
||||||
|
return decodeURIComponent(result.readWrite || result.read);
|
||||||
|
};
|
||||||
|
|
||||||
export const ConnectExplorer: React.FunctionComponent<Props> = ({
|
export const ConnectExplorer: React.FunctionComponent<Props> = ({
|
||||||
setEncryptedToken,
|
setEncryptedToken,
|
||||||
login,
|
login,
|
||||||
|
@ -44,16 +57,8 @@ export const ConnectExplorer: React.FunctionComponent<Props> = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = new Headers();
|
const encryptedToken = await fetchEncryptedToken(connectionString);
|
||||||
headers.append(HttpHeaders.connectionString, connectionString);
|
setEncryptedToken(encryptedToken);
|
||||||
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());
|
|
||||||
setEncryptedToken(decodeURIComponent(result.readWrite || result.read));
|
|
||||||
setAuthType(AuthType.ConnectionString);
|
setAuthType(AuthType.ConnectionString);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { IButtonStyles, ICommandBarStyles, ISeparatorStyles, IStackTokens } from "@fluentui/react";
|
import { IButtonStyles, ICommandBarStyles, ISeparatorStyles, IStackTokens } from "@fluentui/react";
|
||||||
import { StyleConstants } from "../Common/Constants";
|
import { StyleConstants } from "../Common/StyleConstants";
|
||||||
|
|
||||||
export const commandBarItemStyles: IButtonStyles = { root: { paddingLeft: 20 } };
|
export const commandBarItemStyles: IButtonStyles = { root: { paddingLeft: 20 } };
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Platform, configContext } from "./../ConfigContext";
|
||||||
|
|
||||||
export const getDataExplorerWindow = (currentWindow: Window): Window | undefined => {
|
export const getDataExplorerWindow = (currentWindow: Window): Window | undefined => {
|
||||||
// Data explorer is always loaded in an iframe, so traverse the parents until we hit the top and return the first child window.
|
// Data explorer is always loaded in an iframe, so traverse the parents until we hit the top and return the first child window.
|
||||||
try {
|
try {
|
||||||
|
@ -5,7 +7,11 @@ export const getDataExplorerWindow = (currentWindow: Window): Window | undefined
|
||||||
if (currentWindow.parent === currentWindow) {
|
if (currentWindow.parent === currentWindow) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (currentWindow.parent === currentWindow.top) {
|
if (configContext.platform === Platform.Fabric && currentWindow.parent.parent === currentWindow.top) {
|
||||||
|
// in Fabric data explorer is inside an extension iframe, so we have two parent iframes
|
||||||
|
return currentWindow;
|
||||||
|
}
|
||||||
|
if (configContext.platform !== Platform.Fabric && currentWindow.parent === currentWindow.top) {
|
||||||
return currentWindow;
|
return currentWindow;
|
||||||
}
|
}
|
||||||
currentWindow = currentWindow.parent;
|
currentWindow = currentWindow.parent;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { createUri } from "Common/UrlUtility";
|
import { createUri } from "Common/UrlUtility";
|
||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import { fetchEncryptedToken } from "Platform/Hosted/Components/ConnectExplorer";
|
||||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||||
|
import { fetchAccessData } from "hooks/usePortalAccessToken";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
|
@ -60,6 +62,26 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
|
||||||
} else if (platform === Platform.Portal) {
|
} else if (platform === Platform.Portal) {
|
||||||
const explorer = await configurePortal();
|
const explorer = await configurePortal();
|
||||||
setExplorer(explorer);
|
setExplorer(explorer);
|
||||||
|
} else if (platform === Platform.Fabric) {
|
||||||
|
// TODO For now, retrieve info from session storage. Replace with info injected into Data Explorer
|
||||||
|
const connectionString = sessionStorage.getItem("connectionString");
|
||||||
|
if (!connectionString) {
|
||||||
|
console.error("No connection string found in session storage");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const encryptedToken = await fetchEncryptedToken(connectionString);
|
||||||
|
// TODO Duplicated from useTokenMetadata
|
||||||
|
const encryptedTokenMetadata = await fetchAccessData(encryptedToken);
|
||||||
|
|
||||||
|
const win = (window as unknown) as HostedExplorerChildFrame;
|
||||||
|
win.hostedConfig = {
|
||||||
|
authType: AuthType.EncryptedToken,
|
||||||
|
encryptedToken,
|
||||||
|
encryptedTokenMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
const explorer = await configureHosted();
|
||||||
|
setExplorer(explorer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue