updated tabs , sidebar , splas screen

This commit is contained in:
Sakshi Gupta 2025-04-23 18:52:28 +05:30
parent b5976fb034
commit 8dd2571444
10 changed files with 865 additions and 711 deletions

View File

@ -1772,9 +1772,9 @@ input::-webkit-calendar-picker-indicator {
.paddingspan4 { .paddingspan4 {
padding-top: 20px; padding-top: 20px;
padding-left: 20px;
color: white; color: white;
font-size: 14px; padding-left: 25px;
padding-right: 25px;
} }
.closebtnn { .closebtnn {
@ -1914,7 +1914,8 @@ input::-webkit-calendar-picker-indicator::after {
} }
.nav-tabs-margin { .nav-tabs-margin {
background-color: #f2f2f2; background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
.nav-tabs { .nav-tabs {
display: flex; display: flex;
@ -1922,11 +1923,19 @@ input::-webkit-calendar-picker-indicator::after {
align-items: flex-end; align-items: flex-end;
height: 100%; height: 100%;
margin-bottom: -0.5px; margin-bottom: -0.5px;
// border-bottom: 1px solid var(--colorNeutralStroke1);
li { li {
// Override the bootstrap defaults here to align with our layout constants.
margin-bottom: 0px; margin-bottom: 0px;
height: 32px; height: 32px;
&:hover {
background-color: var(--colorNeutralBackground1Hover);
}
&:active {
background-color: var(--colorNeutralBackground1Pressed);
}
} }
} }
} }
@ -1940,8 +1949,20 @@ input::-webkit-calendar-picker-indicator::after {
.nav.nav-tabs.qslevel > li > a:hover { .nav.nav-tabs.qslevel > li > a:hover {
border: none; border: none;
border-radius: 0; border-radius: 0;
background-color: transparent !important; background-color: var(--colorNeutralBackground1Selected) !important;
border-color: transparent; border-color: transparent;
color: var(--colorNeutralForeground1);
}
.nav-tabs > li > .tabNavContentContainer > .tab_Content:hover {
background-color: var(--colorNeutralBackground1Hover);
// border-bottom: 2px solid var(--colorNeutralStroke1);
}
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content,
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content:hover {
background-color: var(--colorNeutralBackground1Selected);
// border-bottom: 2px solid var(--colorCompoundBrandBackground);
} }
.numbersize { .numbersize {
@ -2375,7 +2396,9 @@ a:link {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-width: 0; // This prevents it to grow past the parent's width if its content is too wide min-width: 0;
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
} }
.tabs { .tabs {
@ -2631,9 +2654,10 @@ a:link {
} }
.tabPanesContainer { .tabPanesContainer {
display: flex;
flex-grow: 1; flex-grow: 1;
overflow: hidden; overflow-y: scroll;
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
} }
.tabs-container { .tabs-container {
@ -2655,11 +2679,11 @@ a:link {
.nav-tabs > li.active > .tabNavContentContainer, .nav-tabs > li.active > .tabNavContentContainer,
.nav-tabs > li.active > .tabNavContentContainer:focus, .nav-tabs > li.active > .tabNavContentContainer:focus,
.nav-tabs > li.active > .tabNavContentContainer:hover { .nav-tabs > li.active > .tabNavContentContainer:hover {
color: #555; color: var(--colorNeutralForeground1);
cursor: default; cursor: default;
background-color: @BaseLight; background-color: var(--colorNeutralBackground1Selected);
border-color: @BaseMedium; border-color: var(--colorNeutralStroke1);
border-bottom-color: @BaseLight; // border-bottom-color: var(--colorCompoundBrandBackground);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
height: @ActiveTabHeight; height: @ActiveTabHeight;
@ -2668,7 +2692,7 @@ a:link {
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .contentWrapper > .tabNavText { .nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .contentWrapper > .tabNavText {
font-weight: bolder; font-weight: bolder;
border-bottom: 2px solid rgba(0, 120, 212, 1); border-bottom: 2px solid var(--colorCompoundBrandBackground);
} }
.nav-tabs > li.active:focus > .tabNavContentContainer { .nav-tabs > li.active:focus > .tabNavContentContainer {
@ -2681,7 +2705,7 @@ a:link {
justify-content: space-between; justify-content: space-between;
border-radius: 2px 2px 0 0; border-radius: 2px 2px 0 0;
padding: @DefaultSpace 0px @SmallSpace 0px; padding: @DefaultSpace 0px @SmallSpace 0px;
color: @BaseHigh; color: var(--colorNeutralForeground1);
width: @TabsWidth; width: @TabsWidth;
text-align: center; text-align: center;
position: relative; position: relative;
@ -2689,75 +2713,29 @@ a:link {
&:hover { &:hover {
text-decoration: none; text-decoration: none;
background-color: @BaseMediumLow; background-color: var(--colorNeutralBackground1Hover);
border-color: @BaseMediumLow; border-color: transparent;
} }
&:active { &:active {
background-color: @BaseMediumLow; background-color: var(--colorNeutralBackground1Pressed);
} }
.tab_Content { .tab_Content {
.flex-display(); .flex-display();
width: @TabsWidth; width: @TabsWidth;
border-right: @ButtonBorderWidth solid @BaseMedium; border-right: @ButtonBorderWidth solid var(--colorNeutralStroke1);
white-space: nowrap; white-space: nowrap;
color: var(--colorNeutralForeground1);
.contentWrapper { .contentWrapper {
.flex-display(); .flex-display();
width: @ContentWrapper; width: @ContentWrapper;
.statusIconContainer {
width: @StatusIconContainerSize;
height: @StatusIconContainerSize;
margin-left: @SmallSpace;
display: inline-flex;
.errorIconContainer {
width: @ErrorIconContainer;
height: @ErrorIconContainer;
margin-top: 1px;
.errorIcon {
width: @ErrorIconWidth;
height: @LoadingErrorIconSize;
background-image: url(../images/error_no_outline.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 3px;
display: block;
margin: 1px 0px 0px 6px;
}
}
.errorIconContainer.actionsEnabled {
&:hover {
.hover();
}
&:focus {
.focus();
}
&:active {
.active();
}
}
.errorIconContainer[tabindex]:active {
outline: none;
}
.loadingIcon {
width: @LoadingErrorIconSize;
height: @LoadingErrorIconSize;
margin: 0px 0px @SmallSpace @SmallSpace;
}
}
.tabNavText { .tabNavText {
margin-left: @SmallSpace; margin-left: @SmallSpace;
margin-right: 2px; margin-right: 2px;
color: @BaseDark; color: var(--colorNeutralForeground1);
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@ -2768,21 +2746,36 @@ a:link {
.tabIconSection { .tabIconSection {
width: 29px; width: 29px;
position: relative; position: relative;
padding-top: 2px;
.cancelButton { .cancelButton {
padding: 0px @SmallSpace 0px @SmallSpace; padding: 0px @SmallSpace 0px @SmallSpace;
color: var(--colorNeutralForeground1);
width: 16px;
height: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover { &:hover {
.hover(); background-color: var(--colorNeutralBackground1Hover);
color: var(--colorNeutralForeground1);
} }
&:focus { &:focus {
.focus(); background-color: var(--colorNeutralBackground1Pressed);
color: var(--colorNeutralForeground1);
} }
&:active { &:active {
.active(); background-color: var(--colorNeutralBackground1Pressed);
color: var(--colorNeutralForeground1);
}
&::before {
content: "×";
font-size: 16px;
line-height: 1;
} }
} }
} }
@ -3136,3 +3129,12 @@ a:link {
.sidebarContainer { .sidebarContainer {
height: 100%; height: 100%;
} }
.close-Icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
cursor: pointer;
}

View File

@ -37,12 +37,40 @@ a:focus {
} }
.tabsManagerContainer { .tabsManagerContainer {
background-color: #ffffff; background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
} }
.nav-tabs-margin { .nav-tabs-margin {
padding-top: 5px; padding-top: 5px;
background-color: #ffffff; background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
}
.nav-tabs {
border-bottom: 1px solid var(--colorNeutralStroke1);
color: var(--colorNeutralForeground1);
}
.nav-tabs > li > .tabNavContentContainer > .tab_Content:hover {
border-bottom: 2px solid var(--colorNeutralStroke1);
background-color: var(--colorNeutralBackground1Hover);
}
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content,
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content:hover {
border-bottom: 2px solid var(--colorCompoundBrandBackground);
background-color: var(--colorNeutralBackground1Selected);
}
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .contentWrapper > .tabNavText {
border-bottom: 0px none transparent;
color: var(--colorNeutralForeground1);
}
.tabPanesContainer {
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
} }
.commandBarContainer { .commandBarContainer {
@ -67,24 +95,12 @@ a:focus {
} }
} }
.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;
}
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .contentWrapper > .tabNavText {
border-bottom: 0px none transparent;
}
.tabNavContentContainer { .tabNavContentContainer {
padding: @SmallSpace 0px @SmallSpace 0px; padding: @SmallSpace 0px @SmallSpace 0px;
color: var(--colorNeutralForeground1);
&:hover { &:hover {
background-color: transparent; background-color: var(--colorNeutralBackground1Hover);
border-color: transparent; border-color: transparent;
} }
@ -93,6 +109,7 @@ a:focus {
margin: 0px @SmallSpace 0px @SmallSpace; margin: 0px @SmallSpace 0px @SmallSpace;
width: calc(@TabsWidth - (@SmallSpace * 2)); width: calc(@TabsWidth - (@SmallSpace * 2));
padding-bottom: @SmallSpace; padding-bottom: @SmallSpace;
color: var(--colorNeutralForeground1);
.contentWrapper { .contentWrapper {
.statusIconContainer { .statusIconContainer {
@ -103,17 +120,18 @@ a:focus {
.tabIconSection { .tabIconSection {
.cancelButton { .cancelButton {
padding: 0px 0px 0px @SmallSpace; padding: 0px 0px 0px @SmallSpace;
color: var(--colorNeutralForeground1);
&:hover { &:hover {
background-color: transparent; background-color: var(--colorNeutralBackground1Hover);
} }
&:focus { &:focus {
background-color: transparent; background-color: var(--colorNeutralBackground1Pressed);
} }
&:active { &:active {
background-color: transparent; background-color: var(--colorNeutralBackground1Pressed);
} }
} }
} }

View File

@ -1,270 +1,270 @@
@import "./Common/Constants"; // @import "./Common/Constants";
.resourceTree { // .resourceTree {
height: 100%; // height: 100%;
flex: 0 0 auto; // flex: 0 0 auto;
.main { // .main {
height: 100%; // height: 100%;
} // }
} // }
.resourceTreeScroll { // .resourceTreeScroll {
height: 100%; // height: 100%;
display: flex; // display: flex;
overflow-y: auto; // overflow-y: auto;
overflow-x: hidden; // overflow-x: hidden;
padding-right: 10px; // padding-right: 10px;
} // }
.userSelectNone { // .userSelectNone {
-webkit-user-select: none; // -webkit-user-select: none;
-moz-user-select: none; // -moz-user-select: none;
-ms-user-select: none; // -ms-user-select: none;
} // }
.treeHovermargin { // .treeHovermargin {
margin-left: 16px; // margin-left: 16px;
} // }
.highlight { // .highlight {
padding: @SmallSpace 2px; // padding: @SmallSpace 2px;
outline: 0; // outline: 0;
&:hover { // &:hover {
.hover(); // .hover();
} // }
&:active { // &:active {
.active(); // .active();
} // }
&:focus { // &:focus {
.focus(); // .focus();
} // }
} // }
.contextmenushowing { // .contextmenushowing {
background-color: #eee; // background-color: #eee;
} // }
.collectionstree { // .collectionstree {
width: 100%; // width: 100%;
margin-top: @DefaultSpace; // margin-top: @DefaultSpace;
.databaseList { // .databaseList {
list-style-type: none; // list-style-type: none;
padding-left: 0px; // padding-left: 0px;
.collectionList { // .collectionList {
padding-left: (2 * @MediumSpace); // padding-left: (2 * @MediumSpace);
} // }
.collectionChildList { // .collectionChildList {
padding-left: @LargeSpace; // padding-left: @LargeSpace;
} // }
.databaseDocuments { // .databaseDocuments {
padding-left: (5 * @MediumSpace); // padding-left: (5 * @MediumSpace);
} // }
} // }
} // }
.pointerCursor { // .pointerCursor {
cursor: pointer; // cursor: pointer;
} // }
.menuEllipsis { // .menuEllipsis {
padding-right: 6px; // padding-right: 6px;
font-weight: bold; // font-weight: bold;
font-size: 18px; // font-size: 18px;
position: relative; // position: relative;
top: -5px; // top: -5px;
left: 0px; // left: 0px;
float: right; // float: right;
display: none; // display: none;
padding-left: 6px !important; // padding-left: 6px !important;
line-height: @TreeLineHeight; // line-height: @TreeLineHeight;
} // }
.databaseMenu { // .databaseMenu {
.flex-display(); // .flex-display();
} // }
.databaseMenu:hover .menuEllipsis, // .databaseMenu:hover .menuEllipsis,
.databaseMenu:focus .menuEllipsis { // .databaseMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.databaseCollChildTextOverflow { // .databaseCollChildTextOverflow {
text-overflow: ellipsis; // text-overflow: ellipsis;
white-space: nowrap; // white-space: nowrap;
overflow: hidden; // overflow: hidden;
flex: 1; // flex: 1;
} // }
.collectionMenu { // .collectionMenu {
.flex-display(); // .flex-display();
} // }
.collectionMenu:hover .menuEllipsis, // .collectionMenu:hover .menuEllipsis,
.collectionMenu:focus .menuEllipsis { // .collectionMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.documentsMenu:hover .menuEllipsis, // .documentsMenu:hover .menuEllipsis,
.documentsMenu:focus .menuEllipsis { // .documentsMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.treeChildMenu { // .treeChildMenu {
display: flex; // display: flex;
} // }
.storedProcedureMenu:hover .menuEllipsis, // .storedProcedureMenu:hover .menuEllipsis,
.storedProcedureMenu:focus .menuEllipsis { // .storedProcedureMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.childMenu { // .childMenu {
overflow: hidden; // overflow: hidden;
text-overflow: ellipsis; // text-overflow: ellipsis;
white-space: nowrap; // white-space: nowrap;
padding-left: (6 * @MediumSpace); // padding-left: (6 * @MediumSpace);
width: 100%; // width: 100%;
} // }
.storedChildMenu:hover .menuEllipsis, // .storedChildMenu:hover .menuEllipsis,
.storedChildMenu:focus .menuEllipsis { // .storedChildMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.contextmenu6 { // .contextmenu6 {
top: -29px; // top: -29px;
} // }
.userDefinedMenu:hover .contextmenu6 { // .userDefinedMenu:hover .contextmenu6 {
display: block; // display: block;
} // }
.userDefinedchildMenu:hover .menuEllipsis, // .userDefinedchildMenu:hover .menuEllipsis,
.userDefinedchildMenu:focus .menuEllipsis { // .userDefinedchildMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.triggersMenu:hover .menuEllipsis, // .triggersMenu:hover .menuEllipsis,
.triggersMenu:focus .menuEllipsis { // .triggersMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.triggersChildMenu:hover .menuEllipsis, // .triggersChildMenu:hover .menuEllipsis,
.triggersChildMenu:focus .menuEllipsis { // .triggersChildMenu:focus .menuEllipsis {
display: block; // display: block;
} // }
.databaseId { // .databaseId {
font-size: 14px; // font-size: 14px;
} // }
.storedUdfTriggerMenu { // .storedUdfTriggerMenu {
padding-left: 0px; // padding-left: 0px;
} // }
.collectionstree img { // .collectionstree img {
width: 16px; // width: 16px;
height: 16px; // height: 16px;
vertical-align: text-top; // vertical-align: text-top;
} // }
img.collectionsTreeCollapseExpand { // img.collectionsTreeCollapseExpand {
width: 10px; // width: 10px;
height: 10px; // height: 10px;
vertical-align: middle; // vertical-align: middle;
margin-bottom: 5px; // margin-bottom: 5px;
} // }
.collapsed::before { // .collapsed::before {
content: "\23F5"; // content: "\23F5";
margin-left: 0px; // margin-left: 0px;
font-size: 15px; // font-size: 15px;
} // }
.expanded::before { // .expanded::before {
content: "\23F7"; // content: "\23F7";
margin-left: 0px; // margin-left: 0px;
font-size: 15px; // font-size: 15px;
} // }
.collectionMenuChildren { // .collectionMenuChildren {
padding-left: 42px; // padding-left: 42px;
} // }
.main-nav { // .main-nav {
width: 100vh; // width: 100vh;
height: 40px; // height: 40px;
background: white; // background: white;
transform-origin: left top; // transform-origin: left top;
-webkit-transform-origin: left top; // -webkit-transform-origin: left top;
-ms-transform-origin: left top; // -ms-transform-origin: left top;
transform: rotate(-90deg) translateX(-100%); // transform: rotate(-90deg) translateX(-100%);
-webkit-transform: rotate(-90deg) translateX(-100%); // -webkit-transform: rotate(-90deg) translateX(-100%);
-ms-transform: rotate(-90deg) translateX(-100%); // -ms-transform: rotate(-90deg) translateX(-100%);
border-bottom: 1px solid #ccc; // border-bottom: 1px solid #ccc;
} // }
.main-nav-img { // .main-nav-img {
width: 16px; // width: 16px;
height: 16px; // height: 16px;
margin: -32px 0 0 0; // margin: -32px 0 0 0;
transform: rotate(-90deg) translateX(-100%); // transform: rotate(-90deg) translateX(-100%);
-webkit-transform: rotate(-90deg) translateX(-100%); // -webkit-transform: rotate(-90deg) translateX(-100%);
-ms-transform: rotate(-90deg) translateX(-100%); // -ms-transform: rotate(-90deg) translateX(-100%);
} // }
.main-nav-img.main-nav-sub-img { // .main-nav-img.main-nav-sub-img {
width: 16px; // width: 16px;
height: 16px; // height: 16px;
margin: 0px 0px 0 0; // margin: 0px 0px 0 0;
transform: rotate(180deg) translateX(0%); // transform: rotate(180deg) translateX(0%);
-webkit-transform: rotate(180deg) translateX(0%); // -webkit-transform: rotate(180deg) translateX(0%);
-ms-transform: rotate(180deg) translateX(0%); // -ms-transform: rotate(180deg) translateX(0%);
position: absolute; // position: absolute;
right: -8px; // right: -8px;
top: 16px; // top: 16px;
} // }
ul.nav { // ul.nav {
margin: 0 auto; // margin: 0 auto;
margin-top: 0px; // margin-top: 0px;
margin-left: 0px; // margin-left: 0px;
} // }
.mini ul.nav li { // .mini ul.nav li {
float: right; // float: right;
line-height: 25px; // line-height: 25px;
height: auto; // height: auto;
margin-top: 3px; // margin-top: 3px;
} // }
.spancolchildstyle { // .spancolchildstyle {
padding: 4px; // padding: 4px;
} // }
.contextmenubutton { // .contextmenubutton {
float: right; // float: right;
display: none; // display: none;
} // }
.highlight:hover > .contextmenubutton { // .highlight:hover > .contextmenubutton {
display: unset; // display: unset;
} // }
.highlight:hover > .contextmenubutton::after { // .highlight:hover > .contextmenubutton::after {
content: "\2026"; // content: "\2026";
font-size: 12px; // font-size: 12px;
} // }
.showEllipsis { // .showEllipsis {
text-overflow: ellipsis; // text-overflow: ellipsis;
white-space: nowrap; // white-space: nowrap;
overflow: hidden; // overflow: hidden;
} // }

View File

@ -130,7 +130,6 @@ const useSidebarStyles = makeStyles({
treeContainer: { treeContainer: {
flex: 1, flex: 1,
overflow: "auto", overflow: "auto",
paddingLeft: tokens.spacingHorizontalM,
backgroundColor: tokens.colorNeutralBackground1, backgroundColor: tokens.colorNeutralBackground1,
color: tokens.colorNeutralForeground1, color: tokens.colorNeutralForeground1,
}, },

View File

@ -1,178 +1,178 @@
@import "../../../less/Common/Constants"; // @import "../../../less/Common/Constants";
.splashScreenContainer { // .splashScreenContainer {
width: 100%; // width: 100%;
overflow-y: auto; // overflow-y: scroll;
overflow-x: hidden; // overflow-x: hidden;
.splashScreen { // .splashScreen {
.flex-display(); // .flex-display();
.flex-direction(); // .flex-direction();
text-align: left; // text-align: left;
margin: auto; // margin: auto;
padding-left: 21px; // padding-left: 21px;
padding-right: 16px; // padding-right: 16px;
max-width: 1168px; // max-width: 1168px;
> .title { // > .title {
position: relative; // To attach FeaturePanelLauncher as absolute // position: relative; // To attach FeaturePanelLauncher as absolute
color: @BaseHigh; // color: @BaseHigh;
font-size: 48px; // font-size: 48px;
padding-left: 0px; // padding-left: 0px;
margin: 16px auto; // margin: 16px auto;
text-align: center; // text-align: center;
} // }
> .subtitle { // > .subtitle {
color: @BaseHigh; // color: @BaseHigh;
font-size: 18px; // font-size: 18px;
padding-left: 0px; // padding-left: 0px;
margin: 0px auto; // margin: 0px auto;
text-align: center; // text-align: center;
} // }
.mainButtonsContainer { // .mainButtonsContainer {
.flex-display(); // .flex-display();
text-align: center; // text-align: center;
cursor: pointer; // cursor: pointer;
margin: 40px auto; // margin: 40px auto;
width: 84%; // width: 84%;
> .mainButton { // > .mainButton {
min-width: 124px; // min-width: 124px;
max-width: 296px; // max-width: 296px;
padding: 32px 16px; // padding: 32px 16px;
background-color: @BaseLight; // background-color: @BaseLight;
border: 1px solid #949494; // border: 1px solid #949494;
box-sizing: border-box; // box-sizing: border-box;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); // box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 4px; // border-radius: 4px;
> .legendContainer { // > .legendContainer {
margin-left: 16px; // margin-left: 16px;
text-align: left; // text-align: left;
.legend { // .legend {
font-family: @SemiboldFont; // font-family: @SemiboldFont;
font-size: 18px; // font-size: 18px;
} // }
.description { // .description {
font-size: 10px; // font-size: 10px;
} // }
.newDescription { // .newDescription {
font-size: 13px; // font-size: 13px;
} // }
} // }
} // }
> :nth-child(n + 2) { // > :nth-child(n + 2) {
margin-left: 32px; // margin-left: 32px;
} // }
} // }
.moreStuffContainer { // .moreStuffContainer {
.flex-display(); // .flex-display();
justify-content: space-between; // justify-content: space-between;
.moreStuffColumn { // .moreStuffColumn {
flex-grow: 1; // flex-grow: 1;
flex-basis: 0; // flex-basis: 0;
min-width: 124px; // min-width: 124px;
max-width: 296px; // max-width: 296px;
> .title { // > .title {
font-size: 18px; // font-size: 18px;
font-family: @SemiboldFont; // font-family: @SemiboldFont;
color: @BaseDark; // color: @BaseDark;
padding: 0px; // padding: 0px;
margin-bottom: 16px; // margin-bottom: 16px;
} // }
> ul { // > ul {
list-style: none; // list-style: none;
padding-left: 0px; // padding-left: 0px;
margin-bottom: 0px; // margin-bottom: 0px;
li { // li {
padding: @DefaultSpace; // padding: @DefaultSpace;
.flex-display(); // .flex-display();
align-items: flex-start; // align-items: flex-start;
> img { // > img {
margin-right: @DefaultSpace; // margin-right: @DefaultSpace;
width: 24px; // width: 24px;
height: 24px; // height: 24px;
} // }
.oneLineContent { // .oneLineContent {
margin-top: 4px; // margin-top: 4px;
} // }
.description { // .description {
font-size: 10px; // font-size: 10px;
color: @BaseMediumHigh; // color: @BaseMediumHigh;
} // }
} // }
} // }
.tipContainer { // .tipContainer {
padding: 8px 16px; // padding: 8px 16px;
width: 100%; // width: 100%;
cursor: pointer; // cursor: pointer;
.flex-display(); // .flex-display();
.flex-direction(); // .flex-direction();
> .title { // > .title {
color: @BaseDark; // color: @BaseDark;
padding: 0px; // padding: 0px;
font-size: 12px; // font-size: 12px;
} // }
> .description { // > .description {
color: @BaseDark; // color: @BaseDark;
} // }
&:not(:hover):not(:focus) { // &:not(:hover):not(:focus) {
background-color: @BaseLow; // background-color: @BaseLow;
} // }
} // }
&.commonTasks { // &.commonTasks {
li { // li {
cursor: pointer; // cursor: pointer;
} // }
} // }
&.tipsContainer { // &.tipsContainer {
li { // li {
margin: 2px 0px; // margin: 2px 0px;
} // }
} // }
} // }
} // }
.focusable { // .focusable {
&:hover { // &:hover {
.hover(); // .hover();
} // }
&:focus { // &:focus {
.focus(); // .focus();
} // }
&:active { // &:active {
.active(); // .active();
} // }
} // }
.notebookSplashScreenItem { // .notebookSplashScreenItem {
padding: 12px 0 12px 12px; // padding: 12px 0 12px 12px;
.itemText { // .itemText {
margin-left: 12px; // margin-left: 12px;
font-family: @SemiboldFont; // font-family: @SemiboldFont;
} // }
} // }
} // }
} // }

View File

@ -11,6 +11,7 @@ import {
TeachingBubbleContent, TeachingBubbleContent,
Text, Text,
} from "@fluentui/react"; } from "@fluentui/react";
import { makeStyles, shorthands } from "@fluentui/react-components";
import { sendMessage } from "Common/MessageHandler"; import { sendMessage } from "Common/MessageHandler";
import { MessageTypes } from "Contracts/ExplorerContracts"; import { MessageTypes } from "Contracts/ExplorerContracts";
import { TerminalKind } from "Contracts/ViewModels"; import { TerminalKind } from "Contracts/ViewModels";
@ -33,8 +34,7 @@ import CollectionIcon from "../../../images/tree-collection.svg";
import * as Constants from "../../Common/Constants"; import * as Constants from "../../Common/Constants";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { getCollectionName } from "../../Utils/APITypeUtils"; import { getCollectionName } from "../../Utils/APITypeUtils";
import { FeaturePanelLauncher } from "../Controls/FeaturePanel/FeaturePanelLauncher"; import { useTheme } from "../../hooks/useTheme";
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
import Explorer from "../Explorer"; import Explorer from "../Explorer";
import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity"; import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity";
import { useNotebook } from "../Notebook/useNotebook"; import { useNotebook } from "../Notebook/useNotebook";
@ -55,70 +55,177 @@ export interface SplashScreenProps {
explorer: Explorer; explorer: Explorer;
} }
export class SplashScreen extends React.Component<SplashScreenProps> { const useStyles = makeStyles({
private readonly container: Explorer; splashScreenContainer: {
private subscriptions: Array<{ dispose: () => void }>; display: "flex",
flexDirection: "column",
alignItems: "center",
// justifyContent: "center",
minHeight: "100vh",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
constructor(props: SplashScreenProps) { },
super(props); splashScreen: {
this.container = props.explorer; display: "flex",
this.subscriptions = []; // overflow: "scroll",
} flexDirection: "column",
alignItems: "center",
public componentWillUnmount(): void { textAlign: "left",
while (this.subscriptions.length) { // ...shorthands.padding("40px")
this.subscriptions.pop().dispose(); },
title: {
fontSize: "48px",
fontWeight: "500",
margin: "16px auto",
color: "var(--colorNeutralForeground1)"
},
subtitle: {
fontSize: "18px",
marginBottom: "40px",
color: "var(--colorNeutralForeground2)"
},
cardContainer: {
display: "grid",
gridTemplateColumns: "repeat(2, 1fr)",
gap: "16px",
width: "66%",
margin: "0 auto",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
},
card: {
display: "flex",
flexDirection: "column",
alignItems: "left",
...shorthands.padding("32px", "16px"),
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
border: "1px solid var(--colorNeutralStroke1)",
borderRadius: "4px",
boxShadow: "var(--shadow4)",
cursor: "pointer",
minHeight: "150px",
"&:hover": {
backgroundColor: "var(--colorNeutralBackground1Hover)"
} }
},
cardContent: {
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginLeft: "16px",
textAlign: "left",
color: "var(--colorNeutralForeground1)"
},
cardTitle: {
fontSize: "18px",
fontWeight: "600",
color: "var(--colorNeutralForeground1)",
textAlign: "left",
marginBottom: "8px"
},
cardDescription: {
fontSize: "13px",
color: "var(--colorNeutralForeground2)",
textAlign: "left"
},
moreStuffContainer: {
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gap: "32px",
width: "66%",
margin: "40px auto",
},
moreStuffColumn: {
display: "flex",
flexDirection: "column",
// justifyContent:"space-between"
},
columnTitle: {
fontSize: "20px",
fontWeight: "600",
marginBottom: "16px",
color: "var(--colorNeutralForeground1)"
},
listItem: {
marginBottom: "26px",
},
listItemTitle: {
fontSize: "14px",
color: "var(--colorBrandForegroundLink)",
"&:hover": {
color: "var(--colorBrandForegroundLink)"
}
},
listItemSubtitle: {
fontSize: "13px",
color: "var(--colorNeutralForeground2)"
} }
});
public componentDidMount(): void { export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
this.subscriptions.push( const styles = useStyles();
const { isDarkMode } = useTheme();
const container = explorer;
const subscriptions: Array<{ dispose: () => void }> = [];
React.useEffect(() => {
subscriptions.push(
{ {
dispose: useNotebook.subscribe( dispose: useNotebook.subscribe(
() => this.setState({}), () => setState({}),
(state) => state.isNotebookEnabled, (state) => state.isNotebookEnabled,
), ),
}, },
{ dispose: useSelectedNode.subscribe(() => this.setState({})) }, { dispose: useSelectedNode.subscribe(() => setState({})) },
{ {
dispose: useCarousel.subscribe( dispose: useCarousel.subscribe(
() => this.setState({}), () => setState({}),
(state) => state.showCoachMark, (state) => state.showCoachMark,
), ),
}, },
{ {
dispose: usePostgres.subscribe( dispose: usePostgres.subscribe(
() => this.setState({}), () => setState({}),
(state) => state.showPostgreTeachingBubble, (state) => state.showPostgreTeachingBubble,
), ),
}, },
{ {
dispose: usePostgres.subscribe( dispose: usePostgres.subscribe(
() => this.setState({}), () => setState({}),
(state) => state.showResetPasswordBubble, (state) => state.showResetPasswordBubble,
), ),
}, },
{ {
dispose: useDatabases.subscribe( dispose: useDatabases.subscribe(
() => this.setState({}), () => setState({}),
(state) => state.sampleDataResourceTokenCollection, (state) => state.sampleDataResourceTokenCollection,
), ),
}, },
{ {
dispose: useQueryCopilot.subscribe( dispose: useQueryCopilot.subscribe(
() => this.setState({}), () => setState({}),
(state) => state.copilotEnabled, (state) => state.copilotEnabled,
), ),
}, },
); );
}
private clearMostRecent = (): void => { return () => {
while (subscriptions.length) {
subscriptions.pop().dispose();
}
};
}, []);
const [state, setState] = React.useState({});
const clearMostRecent = () => {
MostRecentActivity.clear(userContext.databaseAccount?.name); MostRecentActivity.clear(userContext.databaseAccount?.name);
this.setState({}); setState({});
}; };
private getSplashScreenButtons = (): JSX.Element => { const getSplashScreenButtons = (): JSX.Element => {
if ( if (
userContext.apiType === "SQL" && userContext.apiType === "SQL" &&
useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotEnabled &&
@ -132,7 +239,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
title={"Launch quick start"} title={"Launch quick start"}
description={"Launch a quick start tutorial to get started with sample data"} description={"Launch a quick start tutorial to get started with sample data"}
onClick={() => { onClick={() => {
this.container.onNewCollectionClicked({ isQuickstart: true }); container.onNewCollectionClicked({ isQuickstart: true });
traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType }); traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType });
}} }}
/> />
@ -141,7 +248,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
title={`New ${getCollectionName()}`} title={`New ${getCollectionName()}`}
description={"Create a new container for storage and throughput"} description={"Create a new container for storage and throughput"}
onClick={() => { onClick={() => {
this.container.onNewCollectionClicked(); container.onNewCollectionClicked();
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType }); traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
}} }}
/> />
@ -177,7 +284,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
); );
} }
const mainItems = this.createMainItems(); const mainItems = createMainItems();
return ( return (
<div className="mainButtonsContainer"> <div className="mainButtonsContainer">
{userContext.apiType === "Postgres" && {userContext.apiType === "Postgres" &&
@ -214,7 +321,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
className="mainButton focusable" className="mainButton focusable"
key={`${item.title}`} key={`${item.title}`}
onClick={item.onClick} onClick={item.onClick}
onKeyPress={(event: React.KeyboardEvent) => this.onSplashScreenItemKeyPress(event, item.onClick)} onKeyPress={(event: React.KeyboardEvent) => onSplashScreenItemKeyPress(event, item.onClick)}
tabIndex={0} tabIndex={0}
role="button" role="button"
> >
@ -267,125 +374,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
); );
}; };
public render(): JSX.Element { const createMainItems = (): SplashScreenItem[] => {
let title: string;
let subtitle: string;
switch (userContext.apiType) {
case "Postgres":
title = "Welcome to Azure Cosmos DB for PostgreSQL";
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
break;
case "VCoreMongo":
title = "Welcome to Azure Cosmos DB for MongoDB (vCore)";
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
break;
default:
title = "Welcome to Azure Cosmos DB";
subtitle = "Globally distributed, multi-model database service for any scale";
}
return (
<div className="connectExplorerContainer">
<form className="connectExplorerFormContainer">
<div className="splashScreenContainer">
<div className="splashScreen">
<h1 className="title" role="heading" aria-label={title}>
{title}
<FeaturePanelLauncher />
</h1>
<div className="subtitle">{subtitle}</div>
{this.getSplashScreenButtons()}
{useCarousel.getState().showCoachMark && (
<Coachmark
target="#quickstartDescription"
positioningContainerProps={{ directionalHint: DirectionalHint.rightTopEdge }}
persistentBeak
>
<TeachingBubbleContent
headline={`Start with sample ${getCollectionName().toLocaleLowerCase()}`}
hasCloseButton
closeButtonAriaLabel="Close"
primaryButtonProps={{
text: "Get started",
onClick: () => {
useCarousel.getState().setShowCoachMark(false);
this.container.onNewCollectionClicked({ isQuickstart: true });
},
}}
secondaryButtonProps={{
text: "Cancel",
onClick: () => useCarousel.getState().setShowCoachMark(false),
}}
onDismiss={() => useCarousel.getState().setShowCoachMark(false)}
>
You will be guided to create a sample container with sample data, then we will give you a tour of
data explorer. You can also cancel launching this tour and explore yourself
</TeachingBubbleContent>
</Coachmark>
)}
{userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo" ? (
<Stack horizontal style={{ margin: "0 auto", width: "84%" }} tokens={{ childrenGap: 16 }}>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Next steps
</Text>
{this.getNextStepItems()}
</Stack>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Tips & learn more
</Text>
{this.getTipsAndLearnMoreItems()}
</Stack>
<Stack style={{ width: "33%" }}></Stack>
</Stack>
) : (
<div className="moreStuffContainer">
<div className="moreStuffColumn commonTasks">
<h2 className="title">Recents</h2>
{this.getRecentItems()}
</div>
<div className="moreStuffColumn">
<h2 className="title">Top 3 things you need to know</h2>
{this.top3Items()}
</div>
<div className="moreStuffColumn tipsContainer">
<h2 className="title">Learning Resources</h2>
{this.getLearningResourceItems()}
</div>
</div>
)}
</div>
</div>
</form>
</div>
);
}
/**
* This exists to enable unit testing
*/
public createDataSampleUtil(): DataSamplesUtil {
return new DataSamplesUtil(this.container);
}
/**
* public for testing purposes
*/
public createMainItems(): SplashScreenItem[] {
const heroes: SplashScreenItem[] = []; const heroes: SplashScreenItem[] = [];
if ( if (
@ -403,7 +392,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") { if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart); useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart);
} else { } else {
this.container.onNewCollectionClicked({ isQuickstart: true }); container.onNewCollectionClicked({ isQuickstart: true });
} }
traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType }); traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType });
}, },
@ -411,18 +400,18 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
heroes.push(launchQuickstartBtn); heroes.push(launchQuickstartBtn);
} }
heroes.push(this.getShellCard()); heroes.push(getShellCard());
heroes.push(this.getThirdCard()); heroes.push(getThirdCard());
return heroes; return heroes;
} };
private getShellCard() { const getShellCard = (): SplashScreenItem => {
if (userContext.apiType === "Postgres") { if (userContext.apiType === "Postgres") {
return { return {
iconSrc: PowerShellIcon, iconSrc: PowerShellIcon,
title: "PostgreSQL Shell", title: "PostgreSQL Shell",
description: "Create table and interact with data using PostgreSQLs shell interface", description: "Create table and interact with data using PostgreSQL's shell interface",
onClick: () => this.container.openNotebookTerminal(TerminalKind.Postgres), onClick: () => container.openNotebookTerminal(TerminalKind.Postgres),
}; };
} }
@ -431,7 +420,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
iconSrc: PowerShellIcon, iconSrc: PowerShellIcon,
title: "Mongo Shell", title: "Mongo Shell",
description: "Create a collection and interact with data using MongoDB's shell interface", description: "Create a collection and interact with data using MongoDB's shell interface",
onClick: () => this.container.openNotebookTerminal(TerminalKind.VCoreMongo), onClick: () => container.openNotebookTerminal(TerminalKind.VCoreMongo),
}; };
} }
@ -440,13 +429,13 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
title: `New ${getCollectionName()}`, title: `New ${getCollectionName()}`,
description: "Create a new container for storage and throughput", description: "Create a new container for storage and throughput",
onClick: () => { onClick: () => {
this.container.onNewCollectionClicked(); container.onNewCollectionClicked();
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType }); traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
}, },
}; };
} };
private getThirdCard() { const getThirdCard = (): SplashScreenItem => {
let icon = ConnectIcon; let icon = ConnectIcon;
let title = "Connect"; let title = "Connect";
let description = "Prefer using your own choice of tooling? Find the connection string you need to connect"; let description = "Prefer using your own choice of tooling? Find the connection string you need to connect";
@ -470,34 +459,34 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
description: description, description: description,
onClick: onClick, onClick: onClick,
}; };
} };
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) { const decorateOpenCollectionActivity = (activity: MostRecentActivity.OpenCollectionItem): SplashScreenItem => {
return { return {
iconSrc: CollectionIcon, iconSrc: CollectionIcon,
title: collectionId, title: activity.collectionId,
description: getCollectionName(), description: getCollectionName(),
onClick: () => { onClick: () => {
const collection = useDatabases.getState().findCollection(databaseId, collectionId); const collection = useDatabases.getState().findCollection(activity.databaseId, activity.collectionId);
collection?.openTab(); collection?.openTab();
}, },
}; };
} };
private decorateOpenNotebookActivity({ name, path }: MostRecentActivity.OpenNotebookItem) { const decorateOpenNotebookActivity = (activity: MostRecentActivity.OpenNotebookItem): SplashScreenItem => {
return { return {
info: path, info: activity.path,
iconSrc: NotebookIcon, iconSrc: NotebookIcon,
title: name, title: activity.name,
description: "Notebook", description: "Notebook",
onClick: () => { onClick: () => {
const notebookItem = this.container.createNotebookContentItemFile(name, path); const notebookItem = container.createNotebookContentItemFile(activity.name, activity.path);
notebookItem && this.container.openNotebook(notebookItem); notebookItem && container.openNotebook(notebookItem);
}, },
}; };
} };
private createRecentItems(): SplashScreenItem[] { const createRecentItems = (): SplashScreenItem[] => {
return MostRecentActivity.getItems(userContext.databaseAccount?.name).map((activity) => { return MostRecentActivity.getItems(userContext.databaseAccount?.name).map((activity) => {
switch (activity.type) { switch (activity.type) {
default: { default: {
@ -505,22 +494,22 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
throw new Error(`Unknown activity: ${unknownActivity}`); throw new Error(`Unknown activity: ${unknownActivity}`);
} }
case MostRecentActivity.Type.OpenNotebook: case MostRecentActivity.Type.OpenNotebook:
return this.decorateOpenNotebookActivity(activity); return decorateOpenNotebookActivity(activity);
case MostRecentActivity.Type.OpenCollection: case MostRecentActivity.Type.OpenCollection:
return this.decorateOpenCollectionActivity(activity); return decorateOpenCollectionActivity(activity);
} }
}); });
} };
private onSplashScreenItemKeyPress(event: React.KeyboardEvent, callback: () => void) { const onSplashScreenItemKeyPress = (event: React.KeyboardEvent, callback: () => void) => {
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) { if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
callback(); callback();
event.stopPropagation(); event.stopPropagation();
} }
} };
private top3Items(): JSX.Element { const top3Items = (): JSX.Element => {
let items: { link: string; title: string; description: string }[]; let items: { link: string; title: string; description: string }[];
switch (userContext.apiType) { switch (userContext.apiType) {
case "SQL": case "SQL":
@ -632,44 +621,54 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
href={item.link} href={item.link}
target="_blank" target="_blank"
style={{ marginRight: 5 }} style={{ marginRight: 5 }}
className={styles.listItemTitle}
> >
{item.title} {item.title}
</Link> </Link>
<Image src={LinkIcon} alt={item.title} /> <Image src={LinkIcon} alt={item.title} />
</Stack> </Stack>
<Text>{item.description}</Text> <Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack> </Stack>
))} ))}
</Stack> </Stack>
); );
} };
private getRecentItems(): JSX.Element { const getRecentItems = (): JSX.Element => {
const recentItems = this.createRecentItems()?.filter((item) => item.description !== "Notebook"); const recentItems = createRecentItems()?.filter((item) => item.description !== "Notebook");
return ( return (
<Stack> <Stack>
<ul> <ul>
{recentItems.map((item, index) => ( {recentItems.map((item, index) => (
<li key={`${item.title}${item.description}${index}`}> <li key={`${item.title}${item.description}${index}`} className={styles.listItem}>
<Stack style={{ marginBottom: 26 }}> <Stack style={{ marginBottom: 26 }}>
<Stack horizontal> <Stack horizontal>
<Image style={{ marginRight: 8 }} src={item.iconSrc} alt={item.title} /> <svg
<Link style={{ fontSize: 14 }} onClick={item.onClick} title={item.info}> width="16"
height="16"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
style={{ marginRight: 8 }}
fill="currentColor"
>
<path d="M4 4c0-1.1.9-2 2-2h3.59c.4 0 .78.16 1.06.44l3.91 3.91c.28.28.44.67.44 1.06V14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 9 6.5V3H6Zm4 .2v3.3c0 .28.22.5.5.5h3.3L10 3.2ZM17 9a1 1 0 0 0-1-1v6a3 3 0 0 1-3 3H6a1 1 0 0 0 1 1h6.06A3.94 3.94 0 0 0 17 14.06V9Z" />
</svg>
<Link style={{ fontSize: 14 }} onClick={item.onClick} title={item.info} className={styles.listItemTitle}>
{item.title} {item.title}
</Link> </Link>
</Stack> </Stack>
<Text style={{ color: "#605E5C" }}>{item.description}</Text> <Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack> </Stack>
</li> </li>
))} ))}
</ul> </ul>
{recentItems.length > 0 && <Link onClick={() => this.clearMostRecent()}>Clear Recents</Link>} {recentItems.length > 0 && <Link onClick={() => clearMostRecent()} className={styles.listItemTitle}>Clear Recents</Link>}
</Stack> </Stack>
); );
} };
private getLearningResourceItems(): JSX.Element { const getLearningResourceItems = (): JSX.Element => {
interface item { interface item {
link: string; link: string;
title: string; title: string;
@ -785,19 +784,20 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
href={item.link} href={item.link}
target="_blank" target="_blank"
style={{ marginRight: 5 }} style={{ marginRight: 5 }}
className={styles.listItemTitle}
> >
{item.title} {item.title}
</Link> </Link>
<Image src={LinkIcon} alt={item.title} /> <Image src={LinkIcon} alt={item.title} />
</Stack> </Stack>
<Text>{item.description}</Text> <Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack> </Stack>
))} ))}
</Stack> </Stack>
); );
} };
private postgresNextStepItems: { link: string; title: string; description: string }[] = [ const postgresNextStepItems: { link: string; title: string; description: string }[] = [
{ {
link: "https://go.microsoft.com/fwlink/?linkid=2208312", link: "https://go.microsoft.com/fwlink/?linkid=2208312",
title: "Data Modeling", title: "Data Modeling",
@ -815,7 +815,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
}, },
]; ];
private vcoreMongoNextStepItems: { link: string; title: string; description: string }[] = [ const vcoreMongoNextStepItems: { link: string; title: string; description: string }[] = [
{ {
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/how-to-migrate-native-tools?tabs=export-import", link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/how-to-migrate-native-tools?tabs=export-import",
title: "Migrate Data", title: "Migrate Data",
@ -833,27 +833,27 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
}, },
]; ];
private getNextStepItems(): JSX.Element { const getNextStepItems = (): JSX.Element => {
const items = userContext.apiType === "Postgres" ? this.postgresNextStepItems : this.vcoreMongoNextStepItems; const items = userContext.apiType === "Postgres" ? postgresNextStepItems : vcoreMongoNextStepItems;
return ( return (
<Stack style={{ minWidth: 124, maxWidth: 296 }}> <Stack style={{ minWidth: 124, maxWidth: 296 }}>
{items.map((item, i) => ( {items.map((item, i) => (
<Stack key={`nextStep${i}`} style={{ marginBottom: 26 }}> <Stack key={`nextStep${i}`} style={{ marginBottom: 26 }}>
<Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}> <Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}>
<Link href={item.link} target="_blank" style={{ marginRight: 5 }}> <Link href={item.link} target="_blank" style={{ marginRight: 5 }} className={styles.listItemTitle}>
{item.title} {item.title}
</Link> </Link>
<Image src={LinkIcon} /> <Image src={LinkIcon} />
</Stack> </Stack>
<Text>{item.description}</Text> <Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack> </Stack>
))} ))}
</Stack> </Stack>
); );
} };
private postgresLearnMoreItems: { link: string; title: string; description: string }[] = [ const postgresLearnMoreItems: { link: string; title: string; description: string }[] = [
{ {
link: "https://go.microsoft.com/fwlink/?linkid=2207226", link: "https://go.microsoft.com/fwlink/?linkid=2207226",
title: "Performance Tuning", title: "Performance Tuning",
@ -871,7 +871,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
}, },
]; ];
private vcoreMongoLearnMoreItems: { link: string; title: string; description: string }[] = [ const vcoreMongoLearnMoreItems: { link: string; title: string; description: string }[] = [
{ {
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/vector-search", link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/vector-search",
title: "Vector Search", title: "Vector Search",
@ -889,23 +889,109 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
}, },
]; ];
private getTipsAndLearnMoreItems(): JSX.Element { const getTipsAndLearnMoreItems = (): JSX.Element => {
const items = userContext.apiType === "Postgres" ? this.postgresLearnMoreItems : this.vcoreMongoLearnMoreItems; const items = userContext.apiType === "Postgres" ? postgresLearnMoreItems : vcoreMongoLearnMoreItems;
return ( return (
<Stack style={{ minWidth: 124, maxWidth: 296 }}> <Stack style={{ minWidth: 124, maxWidth: 296 }}>
{items.map((item, i) => ( {items.map((item, i) => (
<Stack key={`tips${i}`} style={{ marginBottom: 26 }}> <Stack key={`tips${i}`} style={{ marginBottom: 26 }}>
<Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}> <Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}>
<Link href={item.link} target="_blank" style={{ marginRight: 5 }}> <Link href={item.link} target="_blank" style={{ marginRight: 5 }} className={styles.listItemTitle}>
{item.title} {item.title}
</Link> </Link>
<Image src={LinkIcon} /> <Image src={LinkIcon} />
</Stack> </Stack>
<Text>{item.description}</Text> <Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack> </Stack>
))} ))}
</Stack> </Stack>
); );
} };
}
return (
<div className={styles.splashScreenContainer}>
<div className={styles.splashScreen}>
<h1 className={styles.title} role="heading" aria-label="Welcome to Azure Cosmos DB">
Welcome to Azure Cosmos DB<span className="activePatch"></span>
</h1>
<div className={styles.subtitle}>
Globally distributed, multi-model database service for any scale
</div>
{getSplashScreenButtons()}
{useCarousel.getState().showCoachMark && (
<Coachmark
target="#quickstartDescription"
positioningContainerProps={{ directionalHint: DirectionalHint.rightTopEdge }}
persistentBeak
>
<TeachingBubbleContent
headline={`Start with sample ${getCollectionName().toLocaleLowerCase()}`}
hasCloseButton
closeButtonAriaLabel="Close"
primaryButtonProps={{
text: "Get started",
onClick: () => {
useCarousel.getState().setShowCoachMark(false);
container.onNewCollectionClicked({ isQuickstart: true });
},
}}
secondaryButtonProps={{
text: "Cancel",
onClick: () => useCarousel.getState().setShowCoachMark(false),
}}
onDismiss={() => useCarousel.getState().setShowCoachMark(false)}
>
You will be guided to create a sample container with sample data, then we will give you a tour of
data explorer. You can also cancel launching this tour and explore yourself
</TeachingBubbleContent>
</Coachmark>
)}
{userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo" ? (
<Stack horizontal style={{ margin: "0 auto", width: "84%" }} tokens={{ childrenGap: 16 }}>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Next steps
</Text>
{getNextStepItems()}
</Stack>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Tips & learn more
</Text>
{getTipsAndLearnMoreItems()}
</Stack>
<Stack style={{ width: "33%" }}></Stack>
</Stack>
) : (
<div className={styles.moreStuffContainer}>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Recents</h2>
{getRecentItems()}
</div>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Top 3 things you need to know</h2>
{top3Items()}
</div>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Learning Resources</h2>
{getLearningResourceItems()}
</div>
</div>
)}
</div>
</div>
);
};

View File

@ -1,4 +1,5 @@
import { Stack, Text } from "@fluentui/react"; import { Stack, Text } from "@fluentui/react";
import { makeStyles } from "@fluentui/react-components";
import React from "react"; import React from "react";
import { KeyCodes } from "../../Common/Constants"; import { KeyCodes } from "../../Common/Constants";
@ -9,25 +10,50 @@ interface SplashScreenButtonProps {
onClick: () => void; onClick: () => void;
} }
const useStyles = makeStyles({
button: {
border: "1px solid var(--colorNeutralStroke1)",
boxSizing: "border-box",
boxShadow: "var(--shadow4)",
borderRadius: "4px",
padding: "32px 16px",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
width: "100%",
minHeight: "150px",
cursor: "pointer",
"&:hover": {
backgroundColor: "var(--colorNeutralBackground1Hover)"
}
},
content: {
marginLeft: "16px",
textAlign: "left"
},
title: {
fontSize: "18px",
fontWeight: "600",
color: "var(--colorNeutralForeground1)",
marginBottom: "8px"
},
description: {
fontSize: "13px",
color: "var(--colorNeutralForeground2)"
}
});
export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({ export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({
imgSrc, imgSrc,
title, title,
description, description,
onClick, onClick,
}: SplashScreenButtonProps): JSX.Element => { }: SplashScreenButtonProps): JSX.Element => {
const styles = useStyles();
return ( return (
<Stack <Stack
horizontal horizontal
style={{ className={styles.button}
border: "1px solid #949494",
boxSizing: "border-box",
boxShadow: "0 4px 4px rgba(0, 0, 0, 0.25)",
borderRadius: 4,
padding: "32px 16px",
backgroundColor: "#ffffff",
width: "100%",
minHeight: 150,
}}
onClick={onClick} onClick={onClick}
onKeyPress={(event: React.KeyboardEvent) => { onKeyPress={(event: React.KeyboardEvent) => {
if (event.charCode === KeyCodes.Space || event.charCode === KeyCodes.Enter) { if (event.charCode === KeyCodes.Space || event.charCode === KeyCodes.Enter) {
@ -41,9 +67,9 @@ export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({
<div> <div>
<img src={imgSrc} alt={title} aria-hidden="true" /> <img src={imgSrc} alt={title} aria-hidden="true" />
</div> </div>
<Stack style={{ marginLeft: 16 }}> <Stack className={styles.content}>
<Text style={{ fontSize: 18, fontWeight: 600 }}>{title}</Text> <Text className={styles.title}>{title}</Text>
<Text style={{ fontSize: 13 }}>{description}</Text> <Text className={styles.description}>{description}</Text>
</Stack> </Stack>
</Stack> </Stack>
); );

View File

@ -1,3 +1,4 @@
import { Spinner, SpinnerSize } from "@fluentui/react";
import { CollectionTabKind } from "Contracts/ViewModels"; import { CollectionTabKind } from "Contracts/ViewModels";
import Explorer from "Explorer/Explorer"; import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
@ -15,8 +16,6 @@ import { userContext } from "UserContext";
import { useTeachingBubble } from "hooks/useTeachingBubble"; import { useTeachingBubble } from "hooks/useTeachingBubble";
import ko from "knockout"; import ko from "knockout";
import React, { MutableRefObject, useEffect, useRef, useState } from "react"; import React, { MutableRefObject, useEffect, useRef, useState } from "react";
import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
import errorIcon from "../../../images/close-black.svg";
import errorQuery from "../../../images/error_no_outline.svg"; import errorQuery from "../../../images/error_no_outline.svg";
import { useObservable } from "../../hooks/useObservable"; import { useObservable } from "../../hooks/useObservable";
import { ReactTabKind, useTabs } from "../../hooks/useTabs"; import { ReactTabKind, useTabs } from "../../hooks/useTabs";
@ -126,7 +125,17 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
<span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}> <span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}>
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />} {useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
{isTabExecuting(tab, tabKind) && ( {isTabExecuting(tab, tabKind) && (
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" /> <Spinner
size={SpinnerSize.small}
styles={{
circle: {
borderTopColor: "var(--colorNeutralForeground1)",
borderLeftColor: "var(--colorNeutralForeground1)",
borderBottomColor: "var(--colorNeutralForeground1)",
borderRightColor: "var(--colorNeutralBackground1)"
}
}}
/>
)} )}
{isQueryErrorThrown(tab, tabKind) && ( {isQueryErrorThrown(tab, tabKind) && (
<img <img
@ -177,14 +186,11 @@ const CloseButton = ({
onClick={(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => { onClick={(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
event.stopPropagation(); event.stopPropagation();
tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind); tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind);
// tabKind === ReactTabKind.QueryCopilot && useQueryCopilot.getState().resetQueryCopilotStates();
}} }}
tabIndex={active ? 0 : undefined} tabIndex={active ? 0 : undefined}
onKeyPress={({ nativeEvent: e }) => (tab ? tab.onKeyPressClose(undefined, e) : onKeyPressReactTabClose(e, tabKind))} onKeyPress={({ nativeEvent: e }) => (tab ? tab.onKeyPressClose(undefined, e) : onKeyPressReactTabClose(e, tabKind))}
> >
<span className="tabIcon close-Icon"> <span className="tabIcon close-Icon" />
<img src={errorIcon} title="Close" alt="Close" aria-label="hidden" />
</span>
</span> </span>
); );
@ -267,7 +273,6 @@ const isQueryErrorThrown = (tab?: Tab, tabKind?: ReactTabKind): boolean => {
}; };
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => { const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
// eslint-disable-next-line no-console
switch (activeReactTab) { switch (activeReactTab) {
case ReactTabKind.Connect: case ReactTabKind.Connect:
return userContext.apiType === "VCoreMongo" ? ( return userContext.apiType === "VCoreMongo" ? (
@ -292,6 +297,6 @@ const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): J
case ReactTabKind.QueryCopilot: case ReactTabKind.QueryCopilot:
return <QueryCopilotTab explorer={explorer} />; return <QueryCopilotTab explorer={explorer} />;
default: default:
throw Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`); throw new Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
} }
}; };

View File

@ -99,14 +99,26 @@ const App = (): JSX.Element => {
<KeyboardShortcutRoot> <KeyboardShortcutRoot>
<div <div
className="flexContainer" className="flexContainer"
style={{ flex: 1, display: "flex", flexDirection: "column" }} style={{
flex: 1,
display: "flex",
flexDirection: "column",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)"
}}
aria-hidden="false" aria-hidden="false"
data-test="DataExplorerRoot" data-test="DataExplorerRoot"
> >
<div <div
id="divExplorer" id="divExplorer"
className="flexContainer hideOverflows" className="flexContainer hideOverflows"
style={{ flex: 1, display: "flex", flexDirection: "column" }} style={{
flex: 1,
display: "flex",
flexDirection: "column",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)"
}}
> >
<div id="freeTierTeachingBubble"> </div> <div id="freeTierTeachingBubble"> </div>
{/* Main Command Bar - Start */} {/* Main Command Bar - Start */}
@ -119,6 +131,10 @@ const App = (): JSX.Element => {
role="contentinfo" role="contentinfo"
aria-label="Notification console" aria-label="Notification console"
id="explorerNotificationConsole" id="explorerNotificationConsole"
style={{
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)"
}}
> >
<NotificationConsole /> <NotificationConsole />
</div> </div>
@ -144,18 +160,17 @@ const Root: React.FC = () => {
useEffect(() => { useEffect(() => {
if (isDarkMode) { if (isDarkMode) {
document.body.classList.add("isDarkMode"); document.body.classList.add("isDarkMode");
document.body.style.backgroundColor = "var(--colorNeutralBackground1)";
document.body.style.color = "var(--colorNeutralForeground1)";
loadTheme(appThemeFabric); loadTheme(appThemeFabric);
} else { } else {
document.body.classList.remove("isDarkMode"); document.body.classList.remove("isDarkMode");
document.body.style.backgroundColor = "";
document.body.style.color = "";
loadTheme(appThemeFabric); loadTheme(appThemeFabric);
} }
}, [isDarkMode]); }, [isDarkMode]);
// console.log("Root component - Theme state:", {
// isDarkMode,
// currentTheme
// });
return ( return (
<ErrorBoundary> <ErrorBoundary>
<FluentProvider theme={currentTheme}> <FluentProvider theme={currentTheme}>
@ -171,18 +186,21 @@ if (mainElement) {
} }
function LoadingExplorer(): JSX.Element { function LoadingExplorer(): JSX.Element {
const styles = useStyles();
return ( return (
<div className="splashLoaderContainer"> <div className={styles.root}>
<div className="splashLoaderContentContainer"> <div className="splashLoaderContainer">
<p className="connectExplorerContent"> <div className="splashLoaderContentContainer">
<img src={hdeConnectImage} alt="Azure Cosmos DB" /> <p className="connectExplorerContent">
</p> <img src={hdeConnectImage} alt="Azure Cosmos DB" />
<p className="splashLoaderTitle" id="explorerLoadingStatusTitle"> </p>
Welcome to Azure Cosmos DB <p className="splashLoaderTitle" id="explorerLoadingStatusTitle">
</p> Welcome to Azure Cosmos DB
<p className="splashLoaderText" id="explorerLoadingStatusText" role="alert"> </p>
Connecting... <p className="splashLoaderText" id="explorerLoadingStatusText" role="alert">
</p> Connecting...
</p>
</div>
</div> </div>
</div> </div>
); );

View File

@ -66,7 +66,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
} }
}, },
activateNewTab: (tab: TabsBase): void => { activateNewTab: (tab: TabsBase): void => {
set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, activeReactTab: undefined })); set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, activeReactTab: undefined as ReactTabKind | undefined }));
tab.triggerPersistState = get().persistTabsState; tab.triggerPersistState = get().persistTabsState;
tab.onActivate(); tab.onActivate();
get().persistTabsState(); get().persistTabsState();
@ -115,7 +115,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
set({ activeTab: undefined, activeReactTab: undefined }); set({ activeTab: undefined, activeReactTab: undefined });
} }
if (tab.tabId === activeTab.tabId && tabIndex !== -1) { if (activeTab && tab.tabId === activeTab.tabId && tabIndex !== -1) {
const tabToTheRight = updatedTabs[tabIndex]; const tabToTheRight = updatedTabs[tabIndex];
const lastOpenTab = updatedTabs[updatedTabs.length - 1]; const lastOpenTab = updatedTabs[updatedTabs.length - 1];
const newActiveTab = tabToTheRight ?? lastOpenTab; const newActiveTab = tabToTheRight ?? lastOpenTab;