diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d08e84e..b8d5a95 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { DashboardComponent } from './dashboard/dashboard.component'; -import { WebideComponent } from './webide/webide.component'; +import { IdeComponent } from './ide/ide.component'; import { TerminalComponent } from './terminal/terminal.component'; import { MessagesComponent } from './messages/messages.component'; import { TestmessengerComponent } from './testmessenger/testmessenger.component'; @@ -11,7 +11,7 @@ import { config } from './config'; const routes: Routes = [ { path: '', redirectTo: '/dashboard', pathMatch: 'full'}, { path: config.dashboard.route, component: DashboardComponent}, - { path: config.webide.route, component: WebideComponent }, + { path: config.ide.route, component: IdeComponent }, { path: config.terminal.route, component: TerminalComponent }, { path: config.messages.route, component: MessagesComponent }, { path: config.testmessenger.route, component: TestmessengerComponent } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f250712..d829e55 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,10 +9,11 @@ import { AceEditorModule } from 'ng2-ace-editor'; import { AppComponent } from './app.component'; import { DashboardComponent } from './dashboard/dashboard.component'; import { HeaderComponent } from './header/header.component'; +import { SidebarComponent } from './sidebar/sidebar.component'; import { LoginComponent } from './web/web.component'; import { MarkdownService } from './services/markdown.service'; import { TerminadoService } from './services/terminado.service'; -import { WebideComponent } from './webide/webide.component'; +import { IdeComponent } from './ide/ide.component'; import { MessagesComponent } from './messages/messages.component'; import { WebSocketService } from './services/websocket.service'; import { TerminalComponent } from './terminal/terminal.component'; @@ -27,8 +28,9 @@ import { DeploymentNotificationService } from './services/deployment-notificatio declarations: [ AppComponent, HeaderComponent, + SidebarComponent, LoginComponent, - WebideComponent, + IdeComponent, MessagesComponent, TerminalComponent, DashboardComponent, diff --git a/src/app/config.ts b/src/app/config.ts index c6f135d..44b3ab7 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -1,24 +1,45 @@ export const config = { dashboard: { - 'route': 'dashboard', - 'defaultLayout': 'vraw-open' + route: 'dashboard', + currentLayout: 'terminal-ide-web', + enabledLayouts: [ + 'terminal-ide-web', + 'terminal-ide-vertical', + 'terminal-ide-horizontal', + 'terminal-only', + 'terminal-web', + 'ide-web-vertical', + 'ide-only', + 'web-only' + ], + allLayouts: [ + 'terminal-ide-web', + 'terminal-ide-vertical', + 'terminal-ide-horizontal', + 'terminal-only', + 'terminal-web', + 'ide-web-vertical', + 'ide-only', + 'web-only' + ], + hide_messages: false }, - webide: { - 'route': 'webide', - 'autoSaveInterval': 444, - 'defaultCode': 'Loading your file...', - 'defaultLanguage': 'text', - 'deployProcessName': 'login', - 'showDeployButton': true + ide: { + route: 'ide', + autoSaveInterval: 444, + defaultCode: 'Loading your file...', + defaultLanguage: 'text', + deployProcessName: 'login', + showDeployButton: true }, terminal: { - 'route': 'shell' + route: 'shell' }, messages: { - 'route': 'messages', - 'showNextButton': false + route: 'messages', + showNextButton: true }, testmessenger: { - 'route': 'testmessenger' + route: 'testmessenger' } }; diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html index b8c292c..677463a 100755 --- a/src/app/dashboard/dashboard.component.html +++ b/src/app/dashboard/dashboard.component.html @@ -1,17 +1,25 @@ -
-
-
-
-
- +
+
+
+
+
+
+ +
-
-
-
+
+ +
+
+
+
+ +
+ +
-
diff --git a/src/app/dashboard/dashboard.component.scss b/src/app/dashboard/dashboard.component.scss index e166d97..4fe625c 100755 --- a/src/app/dashboard/dashboard.component.scss +++ b/src/app/dashboard/dashboard.component.scss @@ -1,6 +1,23 @@ @import "../../assets/scss/variables.scss"; @import "../../assets/scss/mixins/layout.scss"; +@mixin set-tfw-web($layouts-key) { + + .tfw-web { + .iframe-container { + display: flex; + overflow: hidden; + @include set-component-size($layouts-key, 'web'); + } + + .iframe { + flex-grow: 1; + border: none; + margin: 0; + padding: $small; + } + } +} .tfw-grid-main-components { display: grid; @@ -11,11 +28,11 @@ grid-template-columns: repeat($grid-columns-count, 1fr); grid-template-rows: repeat($grid-rows-count, 1fr); - @include position-grid-items(map_get($layout,$layout-template),'.tfw-'); - .tfw-header, .tfw-messages { - @if ($layout-template == 'default') { + + // Check whether the layout contains a web component + div[class*="web"] & { border: 2px solid $tao-gray-100; border-top: 0; border-left: 0; @@ -33,42 +50,35 @@ padding-top: $hair; background-color: $tao-gray-50; overflow-y: scroll; + max-height: 95vmin; - @if ($layout-template != 'default') { - max-height: 95vmin; - } - @else { + div[class="terminal-ide-web"] & { max-height: 55vmin; } } - .tfw-web { - .iframe-container { - display: flex; - overflow: hidden; - @include set-component-size('web'); - } - - .iframe { - flex-grow: 1; - border: none; - margin: 0; - padding: $small; - } + .tfw-ide { + background-color: $tao-plum-900; } - .tfw-webide { - background-color: $tao-plum-900; + .tfw-sidebar { + background-color: $tao-gray-100; + display: flex; + flex-direction: column; + justify-content: space-between; + padding-top: 75px; + align-items: center; + box-shadow: -15px -5px 19px -11px rgba(0,0,0,0.75); + z-index: 999; + } .tfw-terminal { overflow-y: hidden; - border: 1px solid $tao-plum-100; - border-bottom: 0; background-color: $tao-gray-800; - @if ($layout-template != 'vraw') { - border-left: 0; + div[class*="web"] & { + border-top: 1px solid $tao-plum-100; } } } @@ -77,3 +87,38 @@ filter: blur(3px); } +.terminal-ide-web { + @include set-tfw-web('terminal-ide-web'); + @include position-grid-items(map_get($layouts, 'terminal-ide-web'),'.tfw-'); +} + +.terminal-web { + @include set-tfw-web('terminal-web'); + @include position-grid-items(map_get($layouts,'terminal-web'),'.tfw-'); +} + +.terminal-ide-vertical { + @include position-grid-items(map_get($layouts,'terminal-ide-vertical'),'.tfw-'); +} + +.terminal-ide-horizontal { + @include position-grid-items(map_get($layouts,'terminal-ide-horizontal'),'.tfw-'); +} + +.ide-web-vertical { + @include set-tfw-web('ide-web-vertical'); + @include position-grid-items(map_get($layouts,'ide-web-vertical'),'.tfw-'); +} + +.terminal-only { + @include position-grid-items(map_get($layouts,'terminal-only'),'.tfw-'); +} + +.ide-only { + @include position-grid-items(map_get($layouts,'ide-only'),'.tfw-'); +} + +.web-only { + @include set-tfw-web('web-only'); + @include position-grid-items(map_get($layouts,'web-only'),'.tfw-'); +} diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts index ea2f7b5..716d6a9 100644 --- a/src/app/dashboard/dashboard.component.ts +++ b/src/app/dashboard/dashboard.component.ts @@ -4,6 +4,7 @@ import { Subscription } from 'rxjs/Subscription'; import { WebSocketService } from '../services/websocket.service'; import { LayoutCommand } from './layout-command'; import { config } from '../config'; +import { SidebarComponent } from '../sidebar/sidebar.component'; @Component({ selector: 'app-dashboard', @@ -13,8 +14,10 @@ import { config } from '../config'; export class DashboardComponent implements OnInit, OnDestroy { deploying = false; deploymentNotificationSubscription: Subscription; - layout: string = config.dashboard.defaultLayout; - command_handlers = {'layout': this.layoutHandler.bind(this)}; + layout: string = config.dashboard.currentLayout; + hide_messages: boolean = config.dashboard.hide_messages; + command_handlers = {'layout': this.layoutHandler.bind(this), + 'reload_frontend': this.reloadFrontendHandlder.bind(this)}; constructor(private deploymentNotificationService: DeploymentNotificationService, private webSocketService: WebSocketService, @@ -32,11 +35,25 @@ export class DashboardComponent implements OnInit, OnDestroy { } layoutHandler(data: LayoutCommand) { - if (data.layout.match('vraw-open|vraw-closed|hraw|default-open|default-closed')) { - this.layout = data.layout; + if (config.dashboard.enabledLayouts.includes(data.layout)) { + this.setLayout(data.layout); } else { - console.log('Invalid webide layout "' + data.layout + '" received!'); + console.log('Invalid ide layout "' + data.layout + '" received!'); } + if (data.hide_messages != undefined) { + this.hide_messages = data.hide_messages; + } + } + + reloadFrontendHandlder(data: LayoutCommand) { + setTimeout(() => window.location.reload(), 2000); + } + + setLayout(layout: string) { + this.layout = layout; + // We need to trigger a 'resize' event manually, otherwise ace editor stays collapsed + // Ace editors 'resize' event listener requires a parameter of force=true + setTimeout(() => window.dispatchEvent(new Event('resize', {force: true} as any)), 0); } ngOnDestroy() { diff --git a/src/app/dashboard/layout-command.ts b/src/app/dashboard/layout-command.ts index 3d58db2..87aa41e 100644 --- a/src/app/dashboard/layout-command.ts +++ b/src/app/dashboard/layout-command.ts @@ -1,4 +1,5 @@ export class LayoutCommand { command: string; layout: string; + hide_messages?: boolean; } diff --git a/src/app/webide/webide.component.html b/src/app/ide/ide.component.html similarity index 97% rename from src/app/webide/webide.component.html rename to src/app/ide/ide.component.html index 937d2c1..c4e0e15 100644 --- a/src/app/webide/webide.component.html +++ b/src/app/ide/ide.component.html @@ -1,4 +1,4 @@ -
+
+
+ +
diff --git a/src/app/sidebar/sidebar.component.scss b/src/app/sidebar/sidebar.component.scss new file mode 100755 index 0000000..9d82dce --- /dev/null +++ b/src/app/sidebar/sidebar.component.scss @@ -0,0 +1,32 @@ +@import "../../assets/scss/variables.scss"; +@import "../../assets/scss/mixins/layout.scss"; + +.tfw-ide-pin { + + button { + width: 50px; + height: auto; + padding: 0; + margin-bottom: 50px; + cursor: pointer; + border: 1px solid $tao-gray-200; + transition: all 0.18s ease; + box-shadow: 0 4px 8px 0 rgba($tao-gray-500, 0.2), 0 4px 10px 0 rgba($tao-gray-500, 0.09); + + &:hover, + &:focus, + &:active, + &.active { + box-shadow: 0 4px 8px 0 rgba($tao-blue-500, 0.4), 0 8px 20px 0 rgba($tao-blue-500, 0.39); + border: 1px solid $tao-blue-200; + } + + &:focus, + &:active, + &.active { + outline: none; + width: 48px; + } + + } +} diff --git a/src/app/sidebar/sidebar.component.ts b/src/app/sidebar/sidebar.component.ts new file mode 100644 index 0000000..3b69301 --- /dev/null +++ b/src/app/sidebar/sidebar.component.ts @@ -0,0 +1,21 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { config } from '../config'; + +@Component({ + selector: 'app-sidebar', + templateUrl: './sidebar.component.html', + styleUrls: ['./sidebar.component.scss'] +}) + +export class SidebarComponent { + @Input() layout: string; + @Output() layoutChanged = new EventEmitter(); + enabledLayouts: string[] = config.dashboard.enabledLayouts; + + constructor() {} + + setLayout(layout: string) { + this.layout = layout; + this.layoutChanged.emit(this.layout); + } +} diff --git a/src/app/terminal/terminal.component.scss b/src/app/terminal/terminal.component.scss index 43d03af..9826c02 100644 --- a/src/app/terminal/terminal.component.scss +++ b/src/app/terminal/terminal.component.scss @@ -2,8 +2,6 @@ @import "../../app/dashboard/dashboard.component.scss"; - - .tfw-xterm { max-height: 100%; height: 100%; diff --git a/src/assets/images/ide-only.svg b/src/assets/images/ide-only.svg new file mode 100644 index 0000000..6c3615f --- /dev/null +++ b/src/assets/images/ide-only.svg @@ -0,0 +1 @@ +webideonly \ No newline at end of file diff --git a/src/assets/images/ide-web-vertical.svg b/src/assets/images/ide-web-vertical.svg new file mode 100644 index 0000000..afe4782 --- /dev/null +++ b/src/assets/images/ide-web-vertical.svg @@ -0,0 +1 @@ +web_webide \ No newline at end of file diff --git a/src/assets/images/terminal-ide-horizontal.svg b/src/assets/images/terminal-ide-horizontal.svg new file mode 100644 index 0000000..2d5fecd --- /dev/null +++ b/src/assets/images/terminal-ide-horizontal.svg @@ -0,0 +1 @@ +webide_terminal_horizontal \ No newline at end of file diff --git a/src/assets/images/terminal-ide-vertical.svg b/src/assets/images/terminal-ide-vertical.svg new file mode 100644 index 0000000..bc903ea --- /dev/null +++ b/src/assets/images/terminal-ide-vertical.svg @@ -0,0 +1 @@ +terminal_webide_vertical \ No newline at end of file diff --git a/src/assets/images/terminal-ide-web.svg b/src/assets/images/terminal-ide-web.svg new file mode 100644 index 0000000..5738b3c --- /dev/null +++ b/src/assets/images/terminal-ide-web.svg @@ -0,0 +1 @@ +web_webide_terminal \ No newline at end of file diff --git a/src/assets/images/terminal-only.svg b/src/assets/images/terminal-only.svg new file mode 100644 index 0000000..a0d2267 --- /dev/null +++ b/src/assets/images/terminal-only.svg @@ -0,0 +1 @@ +terminalonly \ No newline at end of file diff --git a/src/assets/images/terminal-web.svg b/src/assets/images/terminal-web.svg new file mode 100644 index 0000000..b2e519a --- /dev/null +++ b/src/assets/images/terminal-web.svg @@ -0,0 +1 @@ +web_terminal \ No newline at end of file diff --git a/src/assets/images/web-only.svg b/src/assets/images/web-only.svg new file mode 100644 index 0000000..8bf90b7 --- /dev/null +++ b/src/assets/images/web-only.svg @@ -0,0 +1 @@ +webonly \ No newline at end of file diff --git a/src/assets/scss/_variables.scss b/src/assets/scss/_variables.scss index 5e07734..aa82644 100644 --- a/src/assets/scss/_variables.scss +++ b/src/assets/scss/_variables.scss @@ -160,5 +160,4 @@ $tao-navbar-height: 67px; $company-logo-width: 130px; -// Change this to switch template. Options: 'default', 'vraw', 'hraw' -$layout-template: 'default'; + diff --git a/src/assets/scss/mixins/_layout.scss b/src/assets/scss/mixins/_layout.scss index 2850480..9a9e604 100644 --- a/src/assets/scss/mixins/_layout.scss +++ b/src/assets/scss/mixins/_layout.scss @@ -1,62 +1,139 @@ -$grid-columns-count: 5; -$grid-rows-count: 15; +$grid-columns-count: 25; +$grid-rows-count: 25; -$default-layout: ( - 'header': (1, 2, 1, 2), - 'messages': (1, 2, 2, 10), - 'webide': (4,$grid-rows-count+1, 1, $grid-rows-count+1), - 'terminal': (1, 4, 10, $grid-rows-count+1), - 'web': (2, 4, 1, 10) +$terminal-ide-web-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2, 16), + 'ide': (15,$grid-columns-count, 1, $grid-rows-count+1), + 'terminal': (1, 15, 16, $grid-rows-count+1), + 'web': (6, 15, 1, 16), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1) ); -$vraw-layout: ( - 'header': (1, 2, 1, 2), - 'messages': (1, 2, 2,$grid-rows-count+1), - 'webide': (4,$grid-rows-count+1, 1,$grid-rows-count+1), - 'terminal': (2, 4, 1,$grid-rows-count+1), +$terminal-web-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2, $grid-columns-count+1), + 'ide': (), + 'terminal': (15, $grid-columns-count, 1, $grid-rows-count+1), + 'web': (6, 15, 1, 10), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1) +); + +$terminal-ide-vertical-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2,$grid-rows-count+1), + 'ide': (15, $grid-columns-count, 1,$grid-rows-count+1), + 'terminal': (6, 15, 1, $grid-rows-count+1), 'web': (), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1) ); -$hraw-layout: ( - 'header': (1, 2, 1,$grid-rows-count+1), - 'messages': (1, 2, 2,$grid-rows-count+1), - 'webide': (2,$grid-rows-count+1, 1, 10), - 'terminal': (2, $grid-rows-count+1, 10, $grid-rows-count+1), +$terminal-ide-horizontal-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2,$grid-rows-count+1), + 'ide': (6,$grid-columns-count, 1, 16), + 'terminal': (6, $grid-columns-count, 16, $grid-rows-count+1), 'web': (), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1), ); -$layout: ( - 'default': $default-layout, - 'vraw': $vraw-layout, - 'hraw': $hraw-layout +$ide-web-vertical-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2, $grid-rows-count+1), + 'ide': (15, $grid-columns-count, 1,$grid-rows-count+1), + 'terminal': (), + 'web': (6, 15, 10, $grid-rows-count+1), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1) ); +$terminal-only-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2,$grid-rows-count+1), + 'ide': (), + 'terminal': (6, $grid-columns-count, 1,$grid-rows-count+1), + 'web': (), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1) +); + +$ide-only-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2,$grid-rows-count+1), + 'ide': (6, $grid-columns-count, 1,$grid-rows-count+1), + 'terminal': (), + 'web': (), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1) +); + +$web-only-layout: ( + 'header': (1, 6, 1, 2), + 'messages': (1, 6, 2,$grid-rows-count+1), + 'ide': (), + 'terminal': (), + 'web': (6, $grid-columns-count, 1,$grid-rows-count+1), + 'sidebar': ($grid-columns-count,$grid-columns-count+1, 1,$grid-rows-count+1) +); + +$layouts: ( + 'terminal-ide-web': $terminal-ide-web-layout, + 'terminal-web': $terminal-web-layout, + 'terminal-ide-vertical': $terminal-ide-vertical-layout, + 'terminal-ide-horizontal': $terminal-ide-horizontal-layout, + 'ide-web-vertical': $ide-web-vertical-layout, + 'terminal-only': $terminal-only-layout, + 'ide-only': $ide-only-layout, + 'web-only': $web-only-layout +); + +@mixin set-layout($layouts-key) { + @if(index(map_keys($layouts), $layouts-key)) { + $layout: map_get($layouts, $layouts-key) + } + @else { + @error 'Invalid layout value: "#{$layouts-key}"' + } +} + +@mixin hide-component() { + // We need to make sure the DOM element is displayed but not visible + visibility: hidden; + position: absolute; + z-index: -10000000; +} + +.hide-attribute { + @include hide-component(); +} + +@function get-layout($layouts-key) { + @return map_get($layouts, $layouts-key); +} @mixin position-grid-items($map, $sel) { $sel: if($sel == '' and &, &, $sel); - @debug $sel; - #{$sel} { - @each $k, $v in $map { - @at-root #{$sel}#{$k} { - @if (length($v) == 0) { - display: none - } - @else { - grid-column-start: nth($v, 1); - grid-column-end: nth($v, 2); - grid-row-start: nth($v, 3); - grid-row-end: nth($v, 4); - } + @each $k, $v in $map { + #{$sel}#{$k} { + @if (length($v) == 0) { + @include hide-component(); + } + @else { + grid-column-start: nth($v, 1); + grid-column-end: nth($v, 2); + grid-row-start: nth($v, 3); + grid-row-end: nth($v, 4); } } } } -@mixin set-component-size($key) { - $columns-count: nth(map_get($default-layout, $key),2) - nth(map_get($default-layout, $key),1); - $rows-count: nth(map_get($default-layout, $key),4) - nth(map_get($default-layout, $key),3); +@mixin set-component-size($layouts-key, $layout-key) { + $tfw-component: map_get(get-layout($layouts-key), $layout-key); - min-width: #{$columns-count / $grid-columns-count * 100}vw; - min-height: #{$rows-count / $grid-rows-count * 100}vh; + @if (length($tfw-component) > 0) { + $columns-count: nth($tfw-component,2) - nth($tfw-component,1); + $rows-count: nth($tfw-component,4) - nth($tfw-component,3); + + min-width: #{$columns-count / $grid-columns-count * 100}vw; + min-height: #{$rows-count / $grid-rows-count * 100}vh; + } } diff --git a/src/assets/scss/shared/_buttons.scss b/src/assets/scss/shared/_buttons.scss index d995931..9215b32 100644 --- a/src/assets/scss/shared/_buttons.scss +++ b/src/assets/scss/shared/_buttons.scss @@ -41,14 +41,23 @@ .tao-btn-rainbow { @extend .tao-btn-primary; background: linear-gradient(135deg, $tao-blue-500 0%, $tao-turqoise-500 70%,$tao-bright-green-300 100%); - transition: all 2s ease; + transition: all 0.18s ease; color: white; border: none; text-decoration: none !important; + box-shadow: 0 4px 8px 0 rgba($tao-blue-500, 0.2), 0 8px 20px 0 rgba($tao-blue-500, 0.29); + + + &:active { + box-shadow: 0 4px 8px 0 rgba($tao-blue-500, 0.1), 0 4px 10px 0 rgba($tao-blue-500, 0.09); + background: linear-gradient(135deg, $tao-blue-500 0%, $tao-turqoise-500 80%,$tao-bright-green-300 100%); + color: white; + padding: 5px 18px; + } &:hover { - box-shadow: none; background: linear-gradient(135deg, $tao-blue-500 0%, $tao-turqoise-500 80%,$tao-bright-green-300 100%); + box-shadow: 0 4px 8px 0 rgba($tao-blue-500, 0.2), 0 8px 20px 0 rgba($tao-blue-500, 0.29); color: white; } }