Merge pull request #20 from avatao-content/pinning-fix
Rebase and fix improve pg's pinning magic
@@ -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 }
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,25 @@
 | 
			
		||||
<div class="tfw-grid-main-components">
 | 
			
		||||
  <div class="tfw-header"><app-header></app-header></div>
 | 
			
		||||
  <div class="tfw-messages"><app-messages></app-messages></div>
 | 
			
		||||
  <div class="tfw-web tao-grid-top-left"
 | 
			
		||||
       [ngClass]="{ 'deploy-blur': deploying }">
 | 
			
		||||
    <div class="iframe-container">
 | 
			
		||||
      <iframe class="iframe"
 | 
			
		||||
              scrolling="yes" frameborder="0"
 | 
			
		||||
              src="about:blank"></iframe>
 | 
			
		||||
<div [attr.class]="layout">
 | 
			
		||||
  <div class="tfw-grid-main-components">
 | 
			
		||||
    <div class="tfw-header"><app-header></app-header></div>
 | 
			
		||||
    <div [ngClass]="{ 'hide-attribute': hide_messages }" class="tfw-messages"><app-messages></app-messages></div>
 | 
			
		||||
    <div class="tfw-web tao-grid-top-left"
 | 
			
		||||
         [ngClass]="{ 'deploy-blur': deploying }">
 | 
			
		||||
      <div class="iframe-container">
 | 
			
		||||
        <iframe class="iframe"
 | 
			
		||||
                scrolling="yes" frameborder="0"
 | 
			
		||||
                src="about:blank"></iframe>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="tfw-webide"><app-webide></app-webide></div>
 | 
			
		||||
  <div class="tfw-terminal">
 | 
			
		||||
    <div class="tfw-ide">
 | 
			
		||||
      <app-ide></app-ide>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="tfw-terminal">
 | 
			
		||||
      <app-terminal></app-terminal>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="tfw-sidebar">
 | 
			
		||||
      <app-sidebar (layoutChanged)="setLayout($event)" [layout]="layout"></app-sidebar>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="tfw-terminal-footer"></div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="tfw-terminal-footer"></div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -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-');
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
export class LayoutCommand {
 | 
			
		||||
    command: string;
 | 
			
		||||
    layout: string;
 | 
			
		||||
    hide_messages?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<div class="tfw-grid-webide-statusbar">
 | 
			
		||||
<div class="tfw-grid-ide-statusbar">
 | 
			
		||||
  <div class="btn-group btn-group-sm flex-wrap tao-grid-center-left">
 | 
			
		||||
    <button *ngFor="let file of files"
 | 
			
		||||
            class="btn tfw-tab-btn"
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
@import "../../assets/scss/variables.scss";
 | 
			
		||||
.tfw-grid-webide-statusbar {
 | 
			
		||||
 | 
			
		||||
.tfw-grid-ide-statusbar {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  height: $tao-navbar-height;
 | 
			
		||||
  grid-template-columns: 8fr 1fr;
 | 
			
		||||
@@ -21,21 +21,21 @@ import { config } from '../config';
 | 
			
		||||
const modelist = brace.acequire('ace/ext/modelist');
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-webide',
 | 
			
		||||
  templateUrl: './webide.component.html',
 | 
			
		||||
  styleUrls: ['./webide.component.scss']
 | 
			
		||||
  selector: 'app-ide',
 | 
			
		||||
  templateUrl: './ide.component.html',
 | 
			
		||||
  styleUrls: ['./ide.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class WebideComponent implements OnInit {
 | 
			
		||||
export class IdeComponent implements OnInit {
 | 
			
		||||
  key_id = 'webide';
 | 
			
		||||
  filename = '';
 | 
			
		||||
  code: string = config.webide.defaultCode;
 | 
			
		||||
  language: string = config.webide.defaultLanguage;
 | 
			
		||||
  code: string = config.ide.defaultCode;
 | 
			
		||||
  language: string = config.ide.defaultLanguage;
 | 
			
		||||
  theme = 'cobalt';
 | 
			
		||||
  directory = '';
 | 
			
		||||
  files: string[];
 | 
			
		||||
  codeState = 'SAVED';
 | 
			
		||||
  deployButtonState = 'DEPLOYED';
 | 
			
		||||
  showDeployButton: boolean = config.webide.showDeployButton;
 | 
			
		||||
  showDeployButton: boolean = config.ide.showDeployButton;
 | 
			
		||||
  autosave = null;
 | 
			
		||||
  command_handlers = {'reload':    this.reloadHandler.bind(this),
 | 
			
		||||
                      'read':      this.readHandler.bind(this),
 | 
			
		||||
@@ -53,8 +53,8 @@ export class WebideComponent implements OnInit {
 | 
			
		||||
    this.subscribeWS();
 | 
			
		||||
    this.requestCode();
 | 
			
		||||
    this.processManagerService.init();
 | 
			
		||||
    this.processManagerService.subscribeCallback(config.webide.deployProcessName, (event) => { this.setDeployButtonState('DEPLOYED'); });
 | 
			
		||||
    this.processManagerService.subscribeErrorCallback(config.webide.deployProcessName, (event) => { this.setDeployButtonState('FAILED'); });
 | 
			
		||||
    this.processManagerService.subscribeCallback(config.ide.deployProcessName, (event) => { this.setDeployButtonState('DEPLOYED'); });
 | 
			
		||||
    this.processManagerService.subscribeErrorCallback(config.ide.deployProcessName, (event) => { this.setDeployButtonState('FAILED'); });
 | 
			
		||||
    this.resetAutoSaveCountdown();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -99,7 +99,7 @@ export class WebideComponent implements OnInit {
 | 
			
		||||
    if (this.autosave) {
 | 
			
		||||
      clearInterval(this.autosave);
 | 
			
		||||
    }
 | 
			
		||||
    this.autosave = setInterval(() => { this.sendCodeIfDirty(); }, config.webide.autoSaveInterval);
 | 
			
		||||
    this.autosave = setInterval(() => { this.sendCodeIfDirty(); }, config.ide.autoSaveInterval);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tabSwitchButtonHandler(file) {
 | 
			
		||||
							
								
								
									
										11
									
								
								src/app/sidebar/sidebar.component.html
									
									
									
									
									
										Executable file
									
								
							
							
						
						@@ -0,0 +1,11 @@
 | 
			
		||||
<div class="tfw-ide-pin" *ngFor="let layoutIter of enabledLayouts">
 | 
			
		||||
 | 
			
		||||
  <div>
 | 
			
		||||
    <button [class.active]="layout === layoutIter"
 | 
			
		||||
            (active)="layout === layoutIter"
 | 
			
		||||
            (focus)="layout === layoutIter"
 | 
			
		||||
            (click)="setLayout(layoutIter)"
 | 
			
		||||
            class="btn"><img src="images/{{layoutIter}}.svg"></button>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										32
									
								
								src/app/sidebar/sidebar.component.scss
									
									
									
									
									
										Executable file
									
								
							
							
						
						@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								src/app/sidebar/sidebar.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -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<string>();
 | 
			
		||||
  enabledLayouts: string[] = config.dashboard.enabledLayouts;
 | 
			
		||||
 | 
			
		||||
  constructor() {}
 | 
			
		||||
 | 
			
		||||
  setLayout(layout: string) {
 | 
			
		||||
    this.layout = layout;
 | 
			
		||||
    this.layoutChanged.emit(this.layout);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,8 +2,6 @@
 | 
			
		||||
@import "../../app/dashboard/dashboard.component.scss";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.tfw-xterm {
 | 
			
		||||
  max-height: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								src/assets/images/ide-only.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.6 KiB  | 
							
								
								
									
										1
									
								
								src/assets/images/ide-web-vertical.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										1
									
								
								src/assets/images/terminal-ide-horizontal.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.4 KiB  | 
							
								
								
									
										1
									
								
								src/assets/images/terminal-ide-vertical.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.4 KiB  | 
							
								
								
									
										1
									
								
								src/assets/images/terminal-ide-web.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										1
									
								
								src/assets/images/terminal-only.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 59"><title>terminalonly</title><path d="M65.06,59H2.94C1.32,59,0,57.35,0,55.31V3.69C0,1.65,1.32,0,2.94,0H65.06C66.68,0,68,1.65,68,3.69V55.31C68,57.35,66.68,59,65.06,59ZM2.94,1.05A2.42,2.42,0,0,0,.84,3.69V55.31A2.42,2.42,0,0,0,2.94,58H65.06a2.42,2.42,0,0,0,2.1-2.64V3.69a2.42,2.42,0,0,0-2.1-2.64Z"/><path d="M18.82,12.75a.16.16,0,0,1,.13.06l12,13.47a1.13,1.13,0,0,1,0,1.58L19,41.33a.18.18,0,0,1-.22,0,.1.1,0,0,1-.05-.17l12-13.47a.93.93,0,0,0,0-1.31L18.68,13a.1.1,0,0,1,.05-.17A.14.14,0,0,1,18.82,12.75Z"/><path d="M18.82,41.76a.52.52,0,0,1-.27-.07.49.49,0,0,1-.26-.35l0-.18.15-.22,12-13.46a.57.57,0,0,0,0-.82L18.28,13.06v-.27a.53.53,0,0,1,.26-.35l.09,0,.18,0a.56.56,0,0,1,.41.18L31.22,26a1.52,1.52,0,0,1,0,2.08l-12,13.47A.54.54,0,0,1,18.82,41.76Zm-.15-.68h0Zm0-28Z"/><path d="M18.82,41.63a.54.54,0,0,1-.21-.05.38.38,0,0,1-.19-.26.32.32,0,0,1,.09-.29l12-13.47a.71.71,0,0,0,0-1l-12-13.46a.34.34,0,0,1-.08-.29.38.38,0,0,1,.19-.26.44.44,0,0,1,.52.09l12,13.47a1.37,1.37,0,0,1,0,1.9l-12,13.47A.41.41,0,0,1,18.82,41.63Zm.05-.28h0Zm-.1-.18Zm.09-28.39h0Z"/><path d="M18.82,42a.79.79,0,0,1-.38-.09.73.73,0,0,1-.39-.52.72.72,0,0,1,.18-.61l12-13.46a.33.33,0,0,0,0-.5l-12-13.47a.68.68,0,0,1-.18-.59.73.73,0,0,1,.39-.53.8.8,0,0,1,1,.17l12,13.47a1.76,1.76,0,0,1,0,2.4l-12,13.47A.77.77,0,0,1,18.82,42Zm0-29.1L30.77,26.33a1.06,1.06,0,0,1,0,1.48l-12,13.47,0,0h0l12-13.46a1,1,0,0,0,0-1.42Z"/><path d="M44.35,41.93H30.14a.75.75,0,1,1,0-1.5H44.35a.75.75,0,0,1,0,1.5Z"/></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
							
								
								
									
										1
									
								
								src/assets/images/terminal-web.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.6 KiB  | 
							
								
								
									
										1
									
								
								src/assets/images/web-only.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.6 KiB  | 
@@ -160,5 +160,4 @@ $tao-navbar-height: 67px;
 | 
			
		||||
$company-logo-width: 130px;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Change this to switch template. Options: 'default', 'vraw', 'hraw'
 | 
			
		||||
$layout-template: 'default';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||