demo shell (ng2)

basic angular2-based demo shell
This commit is contained in:
Denys Vuika
2016-04-06 14:35:46 +01:00
commit f9e1ad80a9
41 changed files with 1803 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
<side-menu #elementsMenu title="Elements">
<form-design-toolbar></form-design-toolbar>
</side-menu>
<side-menu #actionMenu title="Actions">
<div class="p-10">
<button type="button" class="btn btn-success btn-block">
Upload <i class="fa fa-plus"></i>
</button>
<button type="button" class="btn btn-default btn-block">
Folder <i class="fa fa-plus"></i>
</button>
</div>
</side-menu>
<side-menu #propertiesMenu title="Properties" direction="right">
<a href="#">Property 1</a>
<a href="#">Property 2</a>
<a href="#">Property 3</a>
<a href="#">Property 4</a>
<a href="#">Property 5</a>
<a href="#">Property 6</a>
</side-menu>
<app-navbar>
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand navbar-alfresco-logo" [routerLink]="['Home']">
<img src="app/img/blank.gif">
</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li [class.active]="actionMenu.isOpen">
<a href="#" (click)="toggleMenu(actionMenu, $event)" class="image-button">
<i class="fa fa-tasks fa-2x"></i>
</a>
</li>
<li [class.active]="isActive(['Home'])">
<a [routerLink]="['Home']">Home <span class="sr-only">(current)</span></a>
</li>
<!--
<li [class.active]="isActive(['Child1', {id: 1}])">
<a [routerLink]="['Child1', {id: 1}]">Child 1</a>
</li>
<li [class.active]="isActive(['Child2'])">
<a [routerLink]="['Child2']">Child 2</a>
</li>
-->
<li [class.active]="isActive(['Page1'])">
<a [routerLink]="['Page1']">Page 1</a>
</li>
<li [class.active]="isActive(['Page2'])">
<a [routerLink]="['Page2']">Page 2</a>
</li>
<li [class.active]="isActive(['Forms'])">
<a [routerLink]="['Forms']">Forms</a>
</li>
</ul>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search files and folders">
</div>
<button type="submit" class="btn btn-default">
<i class="fa fa-search"></i>
</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-menu-hamburger"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a *ngIf="!isLoggedIn()" [routerLink]="['Login']">Login</a></li>
<li><a *ngIf="isLoggedIn()" href="#" (click)="onLogout($event)">Logout</a></li>
</ul>
</li>
</ul>
<ul *ngIf="isActive(['Forms'])" class="nav navbar-nav navbar-right">
<li [class.active]="elementsMenu.isOpen">
<a href="#" (click)="toggleMenu(elementsMenu, $event)" class="image-button">
<i class="fa fa-cubes fa-2x"></i>
</a>
</li>
<li [class.active]="propertiesMenu.isOpen">
<a href="#" (click)="toggleMenu(propertiesMenu, $event)" class="image-button">
<i class="fa fa-edit fa-2x"></i>
</a>
</li>
</ul>
</div>
</app-navbar>
<!--<router-outlet></router-outlet>-->
<auth-router-outlet></auth-router-outlet>
<!-- Caches -->
<div id="drag-images-cache"></div>

View File

@@ -0,0 +1,67 @@
import {Component} from 'angular2/core';
import {FormService} from './services/form-service';
import {Router, RouteConfig, ROUTER_DIRECTIVES} from "angular2/router";
import {Login} from "./components/login";
import {Authentication} from "./services/authentication";
import {AuthRouterOutlet} from "./components/AuthRouterOutlet";
import {SideMenu} from "./components/core/SideMenu";
import {AppNavBar} from "./components/core/navbar.component";
import {FormDesignToolbar} from "./components/form-design-toolbar.component";
import {HomeView} from "./components/home.view";
import {FormsView} from "./components/forms.view";
import {Page1View} from "./components/page1.view";
import {Page2View} from "./components/page2.view";
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES, AuthRouterOutlet, SideMenu, AppNavBar, FormDesignToolbar],
providers: [FormService]
})
@RouteConfig([
{path: '/', name: 'Home', component: HomeView, useAsDefault: true},
{path: '/login', name: 'Login', component: Login},
{path: '/forms', name: 'Forms', component: FormsView},
{path: '/page1', name: 'Page1', component: Page1View},
{path: '/page2', name: 'Page2', component: Page2View}
])
export class AppComponent {
constructor(
public auth: Authentication,
public router: Router
){}
toggleMenu(menu: SideMenu, $event) {
if (menu) {
menu.toggle();
}
if ($event) {
$event.preventDefault();
}
}
isActive(instruction: any[]): boolean {
return this.router.isRouteActive(this.router.generate(instruction));
}
isLoggedIn(): boolean {
return this.auth.isLoggedIn();
}
onLogout(event) {
event.preventDefault();
this.auth.logout()
.subscribe(
() => this.router.navigate(['Login'])
);
}
/*
hideMenu(menu: SideMenu) {
if (menu && menu.isOpen) {
menu.close();
}
}
*/
}

View File

@@ -0,0 +1,36 @@
import { ElementRef, DynamicComponentLoader, Directive, Attribute } from 'angular2/core';
import { Router, RouterOutlet, ComponentInstruction } from 'angular2/router';
import {Authentication} from '../services/authentication';
@Directive({selector: 'auth-router-outlet'})
export class AuthRouterOutlet extends RouterOutlet {
publicRoutes: Array<string>;
private router: Router;
constructor(
_elementRef: ElementRef, _loader: DynamicComponentLoader,
_parentRouter: Router, @Attribute('name') nameAttr: string,
private authentication: Authentication
) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.router = _parentRouter;
this.publicRoutes = [
'', 'login', 'signup'
];
}
activate(instruction: ComponentInstruction) {
if (this._canActivate(instruction.urlPath)) {
return super.activate(instruction);
}
this.router.navigate(['Login']);
}
_canActivate(url) {
return this.publicRoutes.indexOf(url) !== -1
|| this.authentication.isLoggedIn();
}
}

View File

@@ -0,0 +1,47 @@
import {Component, Input, ElementRef} from "angular2/core";
@Component({
selector: 'side-menu',
host: {
'(click)': 'onClick($event)',
//'(document:click)': 'onOutsideClick($event)'
},
template: `
<nav class="cbp-spmenu cbp-spmenu-vertical cbp-spmenu-{{direction}} inline-menu"
[ngClass]="{ 'cbp-spmenu-open': isOpen }">
<h3>
{{title}}
<a href="#" class="menu-close pull-right" (click)="close()">
<i class="glyphicon glyphicon-remove"></i>
</a>
</h3>
<ng-content></ng-content>
</nav>
`
})
export class SideMenu {
@Input() title: string = '';
@Input() direction: string = 'left';
isOpen: boolean = false;
constructor(private el: ElementRef) {
}
onClick(event) {
event.preventDefault();
event.stopPropagation();
}
toggle() {
this.isOpen = !this.isOpen;
}
open() {
this.isOpen = true;
}
close() {
this.isOpen = false;
}
}

View File

@@ -0,0 +1,24 @@
import {Component} from "angular2/core";
@Component({
selector: 'app-navbar',
template: `
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<ng-content></ng-content>
</div>
</nav>
`,
styles: [
`
:host .image-button {
padding-bottom: 10px;
padding-top: 12px;
max-height: 50px;
}
`
]
})
export class AppNavBar {
}

View File

@@ -0,0 +1,51 @@
import {Component, Input} from 'angular2/core';
@Component({
selector: 'tabs',
template: `
<ul class="nav nav-tabs">
<li *ngFor="#tab of tabs" (click)="selectTab(tab, $event)" [class.active]="tab.active">
<a href="#">{{tab.title}}</a>
</li>
</ul>
<ng-content></ng-content>
`
})
export class Tabs {
tabs: Tab[] = [];
selectTab(tab:Tab, $event) {
this.tabs.forEach(tab => {
tab.active = false;
});
tab.active = true;
if ($event) {
$event.preventDefault();
}
}
addTab(tab:Tab) {
if (this.tabs.length === 0) {
tab.active = true;
}
this.tabs.push(tab);
}
}
@Component({
selector: 'tab',
template: `
<div [hidden]="!active">
<ng-content></ng-content>
</div>
`
})
export class Tab {
@Input('tabTitle') title;
active: Boolean;
constructor(tabs: Tabs) {
tabs.addTab(this);
}
}

View File

@@ -0,0 +1,96 @@
import {Component, ElementRef, OnInit} from "angular2/core";
declare var dropZone: any;
declare var widgets: any;
@Component({
selector: 'form-design-surface',
template: '<div></div>'
})
export class FormDesignSurface implements OnInit {
private _selectedWidget: Element;
constructor(public elementRef: ElementRef) {
//el.nativeElement.style.backgroundColor = 'yellow';
}
get selectedWidget(): Element {
return this._selectedWidget;
}
set selectedWidget(val: Element) {
if (this.selectedWidget && this.selectedWidget != val) {
this._selectedWidget.classList.remove('selected');
}
this._selectedWidget = val;
if (this._selectedWidget) {
this._selectedWidget.classList.add('selected');
}
}
ngOnInit() {
// Create root container
var container = widgets.container.create();
container.dataset.widgetType = 'container';
this.setupWidget(container);
this.elementRef.nativeElement.appendChild(container);
}
private setupWidget(widget: HTMLElement) {
// initialize all drop placeholders
var dropPlaceholders = widget.querySelectorAll('.drop-zone');
for (var i = 0; i < dropPlaceholders.length; i++) {
var placeholder = dropPlaceholders[i];
var z = new dropZone({
element: placeholder,
onDrop: this.onWidgetDrop.bind(this)
});
}
// initialize clicks
if (widget.dataset['widgetId']) {
widget.addEventListener('mouseup', this.onWidgetMouseUp.bind(this), false);
}
// wire child element clicks
var nested = widget.querySelectorAll('[data-widget-id]');
for (var x = 0; x < nested.length; x++) {
nested[x].addEventListener('mouseup', this.onWidgetMouseUp.bind(this), false);
}
}
private onWidgetMouseUp(e) {
var wid = e.currentTarget.dataset.widgetId;
if (wid) {
console.log('Selected Widget Id: ' + wid);
this.selectedWidget = e.currentTarget;
e.stopPropagation();
}
}
private onWidgetDrop(dz, opts) {
var widgetType = opts.widgetType;
if (widgetType) {
var component = widgets[widgetType];
if (component) {
var widget = component.create();
if (widget) {
widget.dataset.widgetType = widgetType;
this.setupWidget(widget);
// insert widget before drop zone
var container = dz.parentElement;
container.insertBefore(widget, dz);
// create new drop zone
var zone = new dropZone({
onDrop: this.onWidgetDrop.bind(this),
minHeight: '5px'
});
// insert new drop zone before widget
container.insertBefore(zone.element, widget);
}
}
}
}
}

View File

@@ -0,0 +1,91 @@
import {Component, OnInit} from "angular2/core";
import {FormService} from "../services/form-service";
@Component({
selector: 'form-design-toolbar',
providers: [FormService],
//encapsulation: ViewEncapsulation.Native,
styles: [`
.category-header {
color: #555;
padding: 11px;
margin: 0;
background: #e7e7e7;
cursor: default;
font-size: 14px;
font-weight: bold;
}
a.toolbar-item {
cursor: move;
display: block;
color: #777;
font-size: 1.1em;
font-weight: 300;
text-decoration: none;
border-bottom: 1px solid #e7e7e7;
padding: 1em;
}
a.toolbar-item:hover {
color: #555;
background: #f8f8f8;
}
a.toolbar-item:active, a.toolbar-item.active {
color: #fff;
border-color: #428bca;
background-color: #428bca;
}
`],
template: `
<template ngFor #category [ngForOf]="categories">
<h3 class="category-header">{{category.name}}</h3>
<a *ngFor="#widget of category.widgets" class="toolbar-item" attr.data-widget-type="{{widget.type}}" draggable="true"
(dragstart)="onElementDragStart($event)" (dragend)="onElementDragEnd($event)">
<i class="{{widget.iconClass || 'fa fa-puzzle-piece'}}"></i> {{widget.name}}
</a>
</template>
`
})
export class FormDesignToolbar implements OnInit {
categories: any[];
private dragCache;
constructor(
private _formService: FormService
) {}
ngOnInit() {
this.categories = this._formService.getWidgetCategories();
// Stores drag ghost elements
this.dragCache = document.getElementById('drag-images-cache');
}
onElementDragStart(e) {
e.dataTransfer.effectAllowed = 'move';
var widgetType = e.target.dataset.widgetType;
var payload = { "widgetType": widgetType };
e.dataTransfer.setData('text', JSON.stringify(payload));
//var dragImage = getDragImage(widgetType);
var dragImage = this._formService.getDragImage(widgetType);
this.dragCache.appendChild(dragImage);
//e.dataTransfer.setDragImage(dragImage, dragImage.offsetWidth / 2, 0);
e.dataTransfer.setDragImage(dragImage, 0, 0);
e.stopPropagation();
}
onElementDragEnd(e) {
if (this.dragCache) {
this.dragCache.innerHTML = '';
}
}
}

View File

@@ -0,0 +1,23 @@
import {Component} from 'angular2/core';
import {FormDesignSurface} from "./form-design-surface.component";
@Component({
selector: 'forms-view',
template: `
<div class="container" style="width:970px;">
<div class="row">
<div class="col-md-12">
<!-- Design surface -->
<form-design-surface></form-design-surface>
</div>
</div>
</div>
`,
directives: [FormDesignSurface]
})
export class FormsView {
/*
@ViewChild(FormDesignSurface)
private _surface: FormDesignSurface;
*/
}

View File

@@ -0,0 +1,17 @@
import {Component} from 'angular2/core';
@Component({
selector: 'home-view',
template: `
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Home View</h1>
</div>
</div>
</div>
`,
directives: []
})
export class HomeView {
}

View File

@@ -0,0 +1,48 @@
import {Component} from "angular2/core";
import {Router, ROUTER_DIRECTIVES} from "angular2/router";
import {FORM_DIRECTIVES, ControlGroup, FormBuilder, Validators} from "angular2/common";
import {Authentication} from "../services/authentication";
@Component({
selector: 'login',
directives: [ROUTER_DIRECTIVES, FORM_DIRECTIVES],
template: `
<div class="row">
<div class="col-md-4 col-md-offset-4">
<form [ngFormModel]="form" (submit)="onSubmit(form.value, $event)">
<div *ngIf="error">Check your password</div>
<div class="form-group">
<label for="username">Username</label>
<input class="form-control" type="text" ngControl="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input class="form-control" type="password" ngControl="password">
</div>
<button type="submit" class="btn btn-default" [disabled]="!form.valid">Login</button>
</form>
</div>
</div>
`
})
export class Login {
form: ControlGroup;
error: boolean = false;
constructor(fb: FormBuilder, public auth: Authentication, public router: Router) {
this.form = fb.group({
username: ['', Validators.required],
password: ['', Validators.required]
});
}
onSubmit(value: any, event) {
//event.preventDefault();
this.auth.login(value.username, value.password)
.subscribe(
//(token: any) => this.router.navigate(['../Home']),
(token: any) => this.router.navigate(['Home']),
() => { this.error = true; }
);
}
}

View File

@@ -0,0 +1,14 @@
import {Component} from "angular2/core";
@Component({
selector: 'page1-view',
template: `
<div class="container">
<div class="row">
<h1>Page 1</h1>
</div>
</div>
`
})
export class Page1View {
}

View File

@@ -0,0 +1,14 @@
import {Component} from "angular2/core";
@Component({
selector: 'page2-view',
template: `
<div class="container">
<div class="row">
<h1>Page 2</h1>
</div>
</div>
`
})
export class Page2View {
}

View File

@@ -0,0 +1,7 @@
body { padding-top: 70px; }
/* Utils */
.p-10 {
padding: 10px;
}

View File

@@ -0,0 +1,116 @@
/* Generated by less 2.2.0 */
.drag-image {
left: 0;
position: absolute;
top: 0;
z-index: -1;
}
.drop-zone {
border-radius: 4px;
visibility: visible;
}
.drop-zone.over {
outline: 1px dashed #ddd;
}
.widget-button {
cursor: default;
}
.widget-button[contenteditable="true"] {
cursor: text;
}
.widget-input:read-only {
background-color: #ffffff;
cursor: default;
}
.widget-row {
background-color: #F5F5F5;
border: 1px solid #dddddd;
border-radius: 4px;
margin: 5px 0;
padding: 25px 14px 0;
position: relative;
}
.widget-row:before {
background-color: #F5F5F5;
border: 1px solid #dddddd;
border-radius: 4px 0 4px 0;
color: #9DA0A4;
content: "Row";
font-size: 12px;
font-weight: bold;
left: -1px;
padding: 3px 7px;
position: absolute;
top: -1px;
}
.widget-row.selected {
border-color: green;
transition: border-color 0.5s;
}
.widget-row.selected:before {
background-color: green;
border-color: green;
color: white;
}
.widget-col {
background-color: #ffffff;
border: 1px solid #dddddd;
border-radius: 4px;
margin: 15px 0;
padding: 39px 19px 24px;
position: relative;
}
.widget-col:before {
background-color: #F5F5F5;
border: 1px solid #dddddd;
border-radius: 4px 0 4px 0;
color: #9DA0A4;
content: "Column";
font-size: 12px;
font-weight: bold;
left: -1px;
padding: 3px 7px;
position: absolute;
top: -1px;
}
.widget-col.selected {
border-color: green;
transition: border-color 0.5s;
}
.widget-col.selected:before {
background-color: green;
border-color: green;
color: white;
}
.widget-container {
border: 1px solid #dddddd;
border-radius: 4px;
margin-left: 0;
margin-top: 10px;
padding: 30px 15px 15px;
position: relative;
width: 100%;
word-wrap: break-word;
}
.widget-container:before {
background-color: #F5F5F5;
border: 1px solid #dddddd;
border-radius: 4px 0 4px 0;
color: #9DA0A4;
content: "Container";
font-size: 12px;
font-weight: bold;
left: -1px;
padding: 3px 7px;
position: absolute;
top: -1px;
}
.widget-container.selected {
border-color: green;
transition: border-color 0.5s;
}
.widget-container.selected:before {
background-color: green;
border-color: green;
color: white;
}

View File

@@ -0,0 +1,201 @@
/* General styles for all menus */
.cbp-spmenu {
background-color: #fff;
position: fixed;
border-color: #e7e7e7;
}
.cbp-spmenu > h3 {
color: #555;
font-size: 1.9em;
padding: 11px;
margin: 0;
font-weight: 300;
background: #e7e7e7;
cursor: default;
}
.cbp-spmenu > a {
display: block;
color: #777;
font-size: 1.1em;
font-weight: 300;
text-decoration: none;
}
.cbp-spmenu > a:hover {
color: #555;
background: #f8f8f8;
}
.cbp-spmenu > a:active,
.cbp-spmenu > a.active {
color: #fff;
border-color: #428bca;
background-color: #428bca;
}
/* Orientation-dependent styles for the content of the menu */
.cbp-spmenu-vertical {
width: 240px;
height: 100%;
top: 0;
/*z-index: 1000;*/
z-index: 1040; /* default TBS navbar index is 1030 */
overflow-y: auto; /* vertical scrollbar */
overflow-x: hidden;
}
.cbp-spmenu-vertical > a {
border-bottom: 1px solid #e7e7e7;
padding: 1em;
}
.cbp-spmenu-horizontal {
width: 100%;
height: 150px;
left: 0;
z-index: 1000;
overflow: hidden;
}
.cbp-spmenu-horizontal > h3 {
height: 100%;
width: 20%;
float: left;
}
.cbp-spmenu-horizontal > a {
float: left;
width: 20%;
padding: 0.8em;
border-left: 1px solid #258ecd;
}
/* Vertical menu that slides from the left or right */
.cbp-spmenu-left {
left: -240px;
border-right: 1px solid #e7e7e7;
}
.cbp-spmenu-right {
right: -240px;
border-left: 1px solid #e7e7e7;
}
.cbp-spmenu-left.cbp-spmenu-open {
left: 0;
}
.cbp-spmenu-right.cbp-spmenu-open {
right: 0;
}
/* Horizontal menu that slides from the top or bottom */
.cbp-spmenu-top {
top: -150px;
}
.cbp-spmenu-bottom {
bottom: -150px;
}
.cbp-spmenu-top.cbp-spmenu-open {
top: 0;
}
.cbp-spmenu-bottom.cbp-spmenu-open {
bottom: 0;
}
/* Push classes applied to the body */
.cbp-spmenu-push {
overflow-x: hidden;
position: relative;
left: 0;
}
.cbp-spmenu-push-toright {
left: 240px;
}
.cbp-spmenu-push-toleft {
left: -240px;
}
/* Transitions */
.cbp-spmenu,
.cbp-spmenu-push {
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
transition: all 0.3s ease;
}
/* Example media queries */
@media screen and (max-width: 55.1875em){
.cbp-spmenu-horizontal {
font-size: 75%;
height: 110px;
}
.cbp-spmenu-top {
top: -110px;
}
.cbp-spmenu-bottom {
bottom: -110px;
}
}
@media screen and (max-height: 26.375em){
.cbp-spmenu-vertical {
font-size: 90%;
width: 190px;
}
.cbp-spmenu-left,
.cbp-spmenu-push-toleft {
left: -190px;
}
.cbp-spmenu-right {
right: -190px;
}
.cbp-spmenu-push-toright {
left: 190px;
}
}
/* Extra stuff */
/* move scrollbar for right menu to the right */
.cbp-spmenu-right { direction: rtl; }
.cbp-spmenu-right > * { direction: ltr; }
/* misc */
a.menu-close {
color: #555;
}
/* Show menu inside main content rather than page */
.inline-menu {
padding-bottom: 50px;
margin-top: 50px;
}
.inline-menu > h3 {
font-size: 14px;
font-weight: bold;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,148 @@
/* Layout */
.drag-image {
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
.drop-zone {
border-radius: 4px;
/*height: 20px;*/
visibility: visible /*!Important*/;
}
.drop-zone.over {
/*height: 100px;*/
outline: 1px dashed #ddd;
}
ul.toolbar {
list-style: none;
margin: 0 0 1em;
padding: 0;
}
ul.toolbar li {
background: #F7F7F7;
border: 1px solid #D9D9D9;
border-radius: 3px;
display: inline-block;
margin: 0 1em 1em 0;
padding: 0.5em 1em;
}
/* Widgets */
/*.widget {}*/
.widget.widget-button {
cursor: default;
}
.widget.widget-button[contentEditable="true"] {
cursor: text;
}
/*.widget.widget-input {}*/
.widget.widget-input:read-only {
background-color: #fff;
cursor: default;
}
/*.widget.widget-input.drag:before {
content: "Input";
}*/
.widget.widget-input:before {
content: "Input";
}
.widget.widget-row {
background-color: #F5F5F5;
border: 1px solid #DDDDDD;
border-radius: 4px;
margin: 5px 0;
padding: 25px 14px 0;
position: relative;
}
.widget.widget-row:before {
background-color: #F5F5F5;
border: 1px solid #DDDDDD;
border-radius: 4px 0 4px 0;
color: #9DA0A4;
content: "Row";
font-size: 12px;
font-weight: bold;
left: -1px;
line-height: 2;
padding: 3px 7px;
position: absolute;
top: -1px;
}
.widget.widget-col {
background-color: #FFFFFF;
border: 1px solid #DDDDDD;
border-radius: 4px;
margin: 15px 0;
padding: 39px 19px 24px;
position: relative;
}
.widget.widget-col:before {
background-color: #F5F5F5;
border: 1px solid #DDDDDD;
border-radius: 4px 0 4px 0;
color: #9DA0A4;
content: "Column";
font-size: 12px;
font-weight: bold;
left: -1px;
padding: 3px 7px;
position: absolute;
top: -1px;
}
.widget.widget-container {
margin-left: 0;
margin-top: 10px;
padding: 30px 15px 15px;
border: 1px solid #DDDDDD;
border-radius: 4px;
position: relative;
width: 100%;
word-wrap: break-word;
}
.widget.widget-container:before {
background-color: #F5F5F5;
border: 1px solid #DDDDDD;
border-radius: 4px 0 4px 0;
color: #9DA0A4;
content: "Container";
font-size: 12px;
font-weight: bold;
left: -1px;
padding: 3px 7px;
position: absolute;
top: -1px;
}
.widget.widget-row.selected,
.widget.widget-container.selected,
.widget.widget-col.selected {
border-color: green;
transition: border-color .5s;
}
.widget.widget-row.selected:before,
.widget.widget-container.selected:before,
.widget.widget-col.selected:before {
border-color: green;
background-color: green;
color: white;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

View File

@@ -0,0 +1,60 @@
function dropZone(opts) {
var options = opts || {},
element = opts.element || document.createElement('div'),
minHeight = opts.minHeight || '20px',
maxHeight = opts.maxHeight || '100px';
element.className = 'drop-zone';
element.style.height = minHeight;
element.textAlign = 'center';
// Event listener for when the dragged element enters the drop zone.
element.addEventListener('dragenter', function (e) {
this.classList.add('over');
this.style.height = maxHeight;
});
// Event listener for when the dragged element is over the drop zone.
element.addEventListener('dragover', function (e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
return false;
});
// Event listener for when the dragged element leaves the drop zone.
element.addEventListener('dragleave', function (e) {
this.classList.remove('over');
this.style.height = minHeight;
});
// Event listener for when the dragged element dropped in the drop zone.
element.addEventListener('drop', function (e) {
if (e.preventDefault) e.preventDefault();
if (e.stopPropagation) e.stopPropagation();
this.classList.remove('over');
this.style.height = minHeight;
if (typeof opts.onDrop === 'function') {
var dropData = e.dataTransfer.getData('text');
var payload = {};
if (dropData) {
try {
payload = JSON.parse(dropData);
} catch (err) {
payload = {};
}
}
opts.onDrop(this, payload);
}
return false;
});
return {
"element": element
};
}

View File

@@ -0,0 +1,9 @@
import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {ROUTER_PROVIDERS} from "angular2/router";
import {Authentication} from "./services/authentication";
bootstrap(AppComponent, [
ROUTER_PROVIDERS,
Authentication
]);

View File

@@ -0,0 +1,32 @@
import {Injectable} from "angular2/core";
import {Observable} from 'rxjs/Rx';
@Injectable()
export class Authentication {
token: string;
constructor() {
this.token = localStorage.getItem('token');
}
isLoggedIn() {
return !!localStorage.getItem('token');
}
login(username: String, password: String) {
if (username === 'test' && password === 'test') {
this.token = 'token';
localStorage.setItem('token', this.token);
return Observable.of('token');
}
return Observable.throw('authentication failure');
}
logout() {
this.token = undefined;
localStorage.removeItem('token');
return Observable.of(true);
}
}

View File

@@ -0,0 +1,55 @@
import {Injectable} from 'angular2/core';
// TODO: move to some namespace
declare var widgets: any;
@Injectable()
export class FormService {
getWidgetCategories() {
var result = [];
var categories = {};
var keys = Object.keys(widgets);
keys.forEach(function (key) {
var w = widgets[key];
var categoryName = w.category || 'Misc';
var category = categories[categoryName];
if (!category) {
category = {
name: categoryName,
widgets: []
};
categories[categoryName] = category;
//result.push(category);
}
category.widgets.push({
type: key,
name: w.name,
iconClass: w.iconClass
});
});
Object.keys(categories).sort().forEach(function (key) {
result.push(categories[key]);
});
return result;
}
getDragImage(widgetType: string): Element {
var w = widgets[widgetType];
// try getting exported drag image
if (w && typeof w.getDragImage === 'function') {
var img = w.getDragImage();
if (img) {
return img;
}
}
// create default drag image
var dragImage = document.createElement('button');
dragImage.className = 'btn btn-default drag-image';
dragImage.textContent = w.name;
dragImage.style.minWidth = '100px';
return dragImage;
}
}

View File

@@ -0,0 +1,16 @@
var widgets = (function (widgets) {
widgets.COMPONENT = {
create: createComponent,
name: 'COMPONENT NAME',
iconClass: 'fa fa-puzzle-piece'
};
function createComponent() {
var widget = document.createElement('button');
widget.textContent = 'Button';
widget.classList.add('btn', 'btn-default', 'widget', 'widget-button');
return widget;
}
return widgets;
})(widgets || {});

View File

@@ -0,0 +1,30 @@
var widgetUtils = (function (utils) {
utils.col = function(className) {
var col = document.createElement('div');
col.dataset.widgetId = utils.uid();
col.classList.add(className, 'widget', 'widget-col');
var dropPlaceholder = document.createElement('div');
dropPlaceholder.className = 'drop-zone';
col.appendChild(dropPlaceholder);
return col;
};
utils.getRandomColor = function () {
var color = '#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6);
return color;
};
utils.uid = (function(){
var id = 0;
return function(){
if (arguments[0] === 0) {
id = 0;
}
return id++;
};
})();
return utils;
})(widgetUtils || {});

View File

@@ -0,0 +1,95 @@
var widgets = (function (widgets, utils) {
widgets.button = {
create: createComponent,
name: 'Button',
iconClass: 'fa fa-square',
category: 'Components'
};
function createComponent() {
var widget = document.createElement('button');
widget.textContent = 'Button';
widget.classList.add('btn', 'btn-default', 'widget', 'widget-button');
widget.dataset.widgetId = utils.uid();
widget.addEventListener('dblclick', function (e) {
if (this.contentEditable !== 'true') {
this.dataset.originalValue = this.textContent;
}
this.contentEditable = true;
selectElementContents(this);
});
widget.addEventListener('blur', function (e) {
if (!this.textContent) {
this.textContent = 'Button';
}
this.contentEditable = false;
});
widget.addEventListener('input', function (e) {
// when button has no text - place the stub and autoselect it
if (!this.textContent) {
this.textContent = 'Button';
selectElementContents(this);
}
});
widget.addEventListener('keydown', function (e) {
// special handling of 'spacebar' key
if (e.keyCode == 32) {
insertHtmlAtCursor('&nbsp;');
e.stopPropagation();
e.preventDefault();
return false;
}
// 'Enter' key
if (e.keyCode == 13) {
e.stopPropagation();
e.preventDefault();
this.blur();
return false;
}
// 'Esc' key
if (e.keyCode == 27) {
var originalValue = this.dataset.originalValue || 'Button';
this.textContent = originalValue;
delete this.dataset.originalValue;
this.blur();
return false;
}
});
return widget;
}
// TODO: move to shared library
function selectElementContents(el) {
var range = document.createRange();
range.selectNodeContents(el);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
// TODO: move to shared library
function insertHtmlAtCursor(html) {
var range, node;
if (window.getSelection && window.getSelection().getRangeAt) {
range = window.getSelection().getRangeAt(0);
node = range.createContextualFragment(html);
range.insertNode(node);
window.getSelection().collapseToEnd();
window.getSelection().modify('move', 'forward', 'character');
} else if (document.selection && document.selection.createRange) {
document.selection.createRange().pasteHTML(html);
document.selection.collapseToEnd();
document.selection.modify('move', 'forward', 'character');
}
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,18 @@
var widgets = (function (widgets, utils) {
widgets.col12 = {
create: createComponent,
name: 'column 12',
iconClass: 'fa fa-puzzle-piece',
category: 'Grid system'
};
function createComponent() {
var widget = document.createElement('div');
widget.dataset.widgetId = utils.uid();
widget.classList.add('row', 'widget', 'widget-row');
widget.appendChild(utils.col('col-md-12'));
return widget;
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,20 @@
var widgets = (function (widgets, utils) {
widgets.col2x6x4 = {
create: createComponent,
name: 'column 2:6:4',
iconClass: 'fa fa-puzzle-piece',
category: 'Grid system'
};
function createComponent() {
var widget = document.createElement('div');
widget.dataset.widgetId = utils.uid();
widget.classList.add('row', 'widget', 'widget-row');
widget.appendChild(utils.col('col-md-2'));
widget.appendChild(utils.col('col-md-6'));
widget.appendChild(utils.col('col-md-4'));
return widget;
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,20 @@
var widgets = (function (widgets, utils) {
widgets.col4x4x4 = {
create: createComponent,
name: 'column 4:4:4',
iconClass: 'fa fa-puzzle-piece',
category: 'Grid system'
};
function createComponent() {
var widget = document.createElement('div');
widget.dataset.widgetId = utils.uid();
widget.classList.add('row', 'widget', 'widget-row');
widget.appendChild(utils.col('col-md-4'));
widget.appendChild(utils.col('col-md-4'));
widget.appendChild(utils.col('col-md-4'));
return widget;
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,19 @@
var widgets = (function (widgets, utils) {
widgets.col6x6 = {
create: createComponent,
name: 'column 6:6',
iconClass: 'fa fa-puzzle-piece',
category: 'Grid system'
};
function createComponent() {
var widget = document.createElement('div');
widget.dataset.widgetId = utils.uid();
widget.classList.add('row', 'widget', 'widget-row');
widget.appendChild(utils.col('col-md-6'));
widget.appendChild(utils.col('col-md-6'));
return widget;
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,20 @@
var widgets = (function (widgets, utils) {
widgets.col8x4 = {
create: createComponent,
name: 'column 8:4',
iconClass: 'fa fa-puzzle-piece',
category: 'Grid system'
};
function createComponent() {
var widget = document.createElement('div');
widget.dataset.widgetId = utils.uid();
widget.classList.add('row', 'widget', 'widget-row');
widget.appendChild(utils.col('col-md-8'));
widget.appendChild(utils.col('col-md-4'));
return widget;
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,22 @@
var widgets = (function (widgets, utils) {
widgets.container = {
create: createComponent,
name: 'container',
iconClass: 'fa fa-puzzle-piece'
};
function createComponent() {
var widget = document.createElement('div');
widget.className = 'container widget widget-container';
widget.dataset.widgetId = utils.uid();
//widget.style.borderColor = utils.getRandomColor();
var dropPlaceholder = document.createElement('div');
dropPlaceholder.className = 'drop-zone';
widget.appendChild(dropPlaceholder);
return widget;
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,71 @@
var widgets = (function (widgets, utils) {
widgets.input = {
create: createComponent,
name: 'Input',
iconClass: 'fa fa-file-text-o',
category: 'Components',
getDragImage: getDragImage
};
function createComponent() {
var widget = document.createElement('input');
widget.type = 'text';
widget.readOnly = true;
widget.classList.add('form-control', 'widget', 'widget-input');
widget.dataset.widgetId = utils.uid();
return widget;
}
/*function getDragImage() {
var container = document.createElement('div');
container.style.width = '200px';
var label = document.createElement('label');
label.textContent = "Input";
label.style.display = 'block';
label.style.margin = '0';
label.style.fontSize = '12px';
container.appendChild(label);
var element = document.createElement('input');
element.className = 'form-control';
element.style.display = 'block';
container.appendChild(element);
return container;
}*/
function getDragImage() {
var element = document.createElement('input');
element.style.width = '200px';
element.className = 'form-control';
return element;
}
// TODO: move to shared library
function moveCaretToEnd(el) {
if (typeof el.selectionStart == "number") {
el.selectionStart = el.selectionEnd = el.value.length;
} else if (typeof el.createTextRange != "undefined") {
el.focus();
var range = el.createTextRange();
range.collapse(false);
range.select();
}
}
// TODO: move to shared library
function setCursorPosition (element, pos) {
if (element.setSelectionRange) {
element.setSelectionRange(pos, pos);
} else if (element.createTextRange) {
var range = element.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
}
return widgets;
})(widgets || {}, widgetUtils);

View File

@@ -0,0 +1,18 @@
var widgets = (function (widgets, utils) {
widgets.row = {
create: createComponent,
name: 'row',
iconClass: 'fa fa-puzzle-piece',
category: 'Grid system'
};
function createComponent() {
var widget = document.createElement('div');
widget.classList.add('row', 'widget', 'widget-row');
widget.dataset.widgetId = utils.uid();
widget.appendChild(utils.col('col-md-12'));
return widget;
}
return widgets;
})(widgets || {}, widgetUtils);