Browse Source

COntinued with UI.

pull/372/head
Sebastian Stehle 7 years ago
parent
commit
ee47bd22c6
  1. 54
      src/Squidex/app-config/webpack.config.js
  2. 24
      src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html
  3. 11
      src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss
  4. 9
      src/Squidex/app/features/settings/pages/workflows/workflow-step.component.ts
  5. 8
      src/Squidex/app/features/settings/pages/workflows/workflows-page.component.html
  6. 31
      src/Squidex/app/features/settings/pages/workflows/workflows-page.component.ts
  7. 12
      src/Squidex/app/framework/angular/forms/color-picker.component.html
  8. 47
      src/Squidex/app/framework/angular/forms/color-picker.component.ts
  9. 10
      src/Squidex/app/framework/angular/forms/dropdown.component.html
  10. 7
      src/Squidex/app/framework/angular/forms/dropdown.component.scss
  11. 15
      src/Squidex/app/framework/angular/forms/dropdown.component.ts
  12. 24
      src/Squidex/app/framework/angular/panel.component.ts
  13. 12
      src/Squidex/app/shared/services/workflows.service.ts

54
src/Squidex/app-config/webpack.config.js

@ -28,12 +28,12 @@ const plugins = {
TsLintPlugin: require('tslint-webpack-plugin') TsLintPlugin: require('tslint-webpack-plugin')
}; };
module.exports = function(env) { module.exports = function (env) {
const isDevServer = path.basename(require.main.filename) === 'webpack-dev-server.js'; const isDevServer = path.basename(require.main.filename) === 'webpack-dev-server.js';
const isProduction = env && env.production; const isProduction = env && env.production;
const isTests = env && env.target === 'tests'; const isTests = env && env.target === 'tests';
const isCoverage = env && env.coverage; const isCoverage = env && env.coverage;
const isJit = env && env.jit; const isAot = isProduction;
const config = { const config = {
mode: isProduction ? 'production' : 'development', mode: isProduction ? 'production' : 'development',
@ -56,7 +56,7 @@ module.exports = function(env) {
* *
* See: https://webpack.js.org/configuration/resolve/#resolve-extensions * See: https://webpack.js.org/configuration/resolve/#resolve-extensions
*/ */
extensions: ['.js', '.mjs', '.ts', '.css', '.scss'], extensions: ['.ts', '.js', '.mjs', '.css', '.scss'],
modules: [ modules: [
root('app'), root('app'),
root('app', 'theme'), root('app', 'theme'),
@ -181,8 +181,6 @@ module.exports = function(env) {
} }
}; };
console.log(JSON.stringify(config, null, 2));
if (!isTests) { if (!isTests) {
/** /**
* The entry point for the bundle. Our Angular app. * The entry point for the bundle. Our Angular app.
@ -258,6 +256,15 @@ module.exports = function(env) {
waitForLinting: isProduction waitForLinting: isProduction
}) })
); );
config.plugins.push(
new plugins.NgToolsWebpack.AngularCompilerPlugin({
entryModule: 'app/app.module#AppModule',
sourceMap: !isProduction,
skipSourceGeneration: !isAot,
tsConfigPath: './tsconfig.json'
})
);
} }
if (isProduction) { if (isProduction) {
@ -284,14 +291,8 @@ module.exports = function(env) {
}; };
} }
if (!isCoverage) { if (isCoverage) {
config.module.rules.push({ // Do not instrument tests.
test: /\.ts$/,
use: [{
loader: 'awesome-typescript-loader', options: { useCache: true, useBabel: true }
}]
})
} else {
config.module.rules.push({ config.module.rules.push({
test: /\.ts$/, test: /\.ts$/,
use: [{ use: [{
@ -300,7 +301,7 @@ module.exports = function(env) {
include: [/\.(e2e|spec)\.ts$/], include: [/\.(e2e|spec)\.ts$/],
}); });
// Use instrument loader for all normal builds. // Use instrument loader for all normal files.
config.module.rules.push({ config.module.rules.push({
test: /\.ts$/, test: /\.ts$/,
use: [{ use: [{
@ -310,6 +311,13 @@ module.exports = function(env) {
}], }],
exclude: [/\.(e2e|spec)\.ts$/] exclude: [/\.(e2e|spec)\.ts$/]
}); });
} else {
config.module.rules.push({
test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
use: [{
loader: plugins.NgToolsWebpack.NgToolsLoader
}]
})
} }
if (isProduction) { if (isProduction) {
@ -349,23 +357,5 @@ module.exports = function(env) {
}); });
} }
if (!isJit) {
config.module.rules.push({
test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
use: [{
loader: '@ngtools/webpack'
}]
});
config.plugins.push(
new plugins.NgToolsWebpack.AngularCompilerPlugin({
entryModule: 'app/app.module#AppModule',
sourceMap: !isProduction,
skipSourceGeneration: false,
tsConfigPath: './tsconfig.json'
})
);
}
return config; return config;
}; };

24
src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html

@ -2,8 +2,10 @@
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-auto color pr-2"> <div class="col-auto color pr-2">
<sqx-color-picker mode="Circle" <sqx-color-picker mode="Circle"
[ngModelOptions]="onBlur"
[ngModel]="step.color" [ngModel]="step.color"
(ngModelChange)="changeColor($event)"> (ngModelChange)="changeColor($event)"
[disabled]="step.isLocked">
</sqx-color-picker> </sqx-color-picker>
</div> </div>
<div class="col"> <div class="col">
@ -20,18 +22,22 @@
</div> </div>
</div> </div>
<div class="row transition no-gutters"> <div class="row transition no-gutters" *ngFor="let transition of transitions">
<div class="col-auto"> <div class="col-auto">
<i class="icon-arrow-right text-decent"></i> <i class="icon-arrow-right text-decent"></i>
</div> </div>
<div class="col-3 pl-2"> <div class="col-3 pl-2">
<div class="color-circle" [style.background]="'red'"></div> In Progress <div class="transition-to">
<div class="color-circle" [style.background]="transition.step.color"></div> {{transition.to}}
</div>
</div> </div>
<div class="col pl-2"> <div class="col pl-2">
<input class="form-control" placeholder="Expression" /> <button class="btn btn-block btn-outline-secondary dashed">
Add Expression
</button>
</div> </div>
<div class="col-auto pl-2"> <div class="col-auto pl-2">
<button type="button" class="btn btn-text-danger" (click)="remove.emit()"> <button type="button" class="btn btn-text-danger" (click)="transitionRemove.emit(transition)">
<i class="icon-bin2"></i> <i class="icon-bin2"></i>
</button> </button>
</div> </div>
@ -42,17 +48,15 @@
<i class="icon-arrow-right text-decent"></i> <i class="icon-arrow-right text-decent"></i>
</div> </div>
<div class="col-3 pl-2"> <div class="col-3 pl-2">
<sqx-dropdown [items]="openSteps"> <sqx-dropdown [items]="openSteps" [(ngModel)]="openStep">
<ng-template let-target="$implicit"> <ng-template let-target="$implicit">
<span class="autocomplete-user">
<div class="color-circle" [style.background]="target.color"></div> {{target.name}} <div class="color-circle" [style.background]="target.color"></div> {{target.name}}
</span>
</ng-template> </ng-template>
</sqx-dropdown> </sqx-dropdown>
</div> </div>
<div class="col pl-2"> <div class="col pl-2">
<button class="btn btn-success"> <button class="btn btn-outline-secondary" (click)="transitionAdd.emit(openStep)">
Add Transition <i class="icon-plus"></i>
</button> </button>
</div> </div>
</div> </div>

11
src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss

@ -12,6 +12,10 @@
display: inline-block; display: inline-block;
} }
.dashed {
border-style: dashed;
}
.transition { .transition {
& { & {
padding-left: 1rem; padding-left: 1rem;
@ -19,6 +23,13 @@
margin-bottom: .5rem; margin-bottom: .5rem;
line-height: 2rem; line-height: 2rem;
} }
&-to {
padding: .5rem .75rem;
background: transparent;
border: 1px solid transparent;
line-height: 1.2rem;
}
} }
.step { .step {

9
src/Squidex/app/features/settings/pages/workflows/workflow-step.component.ts

@ -27,6 +27,9 @@ export class WorkflowStepComponent implements OnChanges {
@Input() @Input()
public step: WorkflowStep; public step: WorkflowStep;
@Output()
public transitionAdd = new EventEmitter<WorkflowStep>();
@Output() @Output()
public transitionRemove = new EventEmitter<WorkflowTransition>(); public transitionRemove = new EventEmitter<WorkflowTransition>();
@ -39,13 +42,17 @@ export class WorkflowStepComponent implements OnChanges {
@Output() @Output()
public remove = new EventEmitter(); public remove = new EventEmitter();
public onBlur = { updateOn: 'blur' };
public openSteps: WorkflowStep[]; public openSteps: WorkflowStep[];
public openStep: WorkflowStep;
public transitions: WorkflowTransitionView[]; public transitions: WorkflowTransitionView[];
public ngOnChanges(changes: SimpleChanges) { public ngOnChanges(changes: SimpleChanges) {
if (changes['workflow'] || changes['step']) { if (changes['workflow'] || changes['step'] || false) {
this.openSteps = this.workflow.getOpenSteps(this.step); this.openSteps = this.workflow.getOpenSteps(this.step);
this.openStep = this.openSteps[0];
this.transitions = this.workflow.getTransitions(this.step); this.transitions = this.workflow.getTransitions(this.step);
} }

8
src/Squidex/app/features/settings/pages/workflows/workflows-page.component.html

@ -20,12 +20,14 @@
</ng-container> </ng-container>
<ng-container content> <ng-container content>
<sqx-workflow-step *ngFor="let step of workflow.steps" <sqx-workflow-step *ngFor="let step of workflow.steps; trackBy: trackByStep"
[workflow]="workflow" [workflow]="workflow"
[step]="step" [step]="step"
(update)="updateStep(step, $event)"
(rename)="renameStep(step, $event)" (rename)="renameStep(step, $event)"
(remove)="removeStep(step)"> (remove)="removeStep(step)"
(transitionAdd)="addTransiton(step, $event)"
(transitionRemove)="removeTransition(step, $event)"
(update)="updateStep(step, $event)">
</sqx-workflow-step> </sqx-workflow-step>
<button class="btn btn-success" (click)="addStep()"> <button class="btn btn-success" (click)="addStep()">

31
src/Squidex/app/features/settings/pages/workflows/workflows-page.component.ts

@ -5,12 +5,13 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { import {
WorkflowDto, WorkflowDto,
WorkflowStep, WorkflowStep,
WorkflowStepValues WorkflowStepValues,
WorkflowTransition
} from '@app/shared'; } from '@app/shared';
@Component({ @Component({
@ -18,8 +19,12 @@ import {
styleUrls: ['./workflows-page.component.scss'], styleUrls: ['./workflows-page.component.scss'],
templateUrl: './workflows-page.component.html' templateUrl: './workflows-page.component.html'
}) })
export class WorkflowsPageComponent { export class WorkflowsPageComponent implements OnInit {
public workflow = new WorkflowDto().setStep('Published', { color: 'green' }); public workflow: WorkflowDto;
public ngOnInit() {
this.workflow = new WorkflowDto().setStep('Published', { color: 'green', isLocked: true });
}
public reload() { public reload() {
return; return;
@ -33,16 +38,28 @@ export class WorkflowsPageComponent {
this.workflow = this.workflow.setStep(`Step${this.workflow.steps.length + 1}`, {}); this.workflow = this.workflow.setStep(`Step${this.workflow.steps.length + 1}`, {});
} }
public addTransiton(from: WorkflowStep, to: WorkflowStep) {
this.workflow = this.workflow.setTransition(from.name, to.name, {});
}
public removeTransition(from: WorkflowStep, transition: WorkflowTransition) {
this.workflow = this.workflow.removeTransition(from.name, transition.to);
}
public updateStep(step: WorkflowStep, values: WorkflowStepValues) { public updateStep(step: WorkflowStep, values: WorkflowStepValues) {
// this.workflow = this.workflow.setStep(step.name, values); this.workflow = this.workflow.setStep(step.name, values);
} }
public renameStep(step: WorkflowStep, newName: string) { public renameStep(step: WorkflowStep, newName: string) {
// this.workflow = this.workflow.renameStep(step.name, newName); this.workflow = this.workflow.renameStep(step.name, newName);
} }
public removeStep(step: WorkflowStep) { public removeStep(step: WorkflowStep) {
// this.workflow = this.workflow.removeStep(step.name); this.workflow = this.workflow.removeStep(step.name);
}
public trackByStep(index: number, step: WorkflowStep) {
return step.name;
} }
} }

12
src/Squidex/app/framework/angular/forms/color-picker.component.html

@ -5,21 +5,23 @@
[style.color]="snapshot.foreground" [style.color]="snapshot.foreground"
[placeholder]="placeholder" [placeholder]="placeholder"
[ngModel]="snapshot.value" [ngModel]="snapshot.value"
(ngModelChange)="writeValue($event)" (ngModelChange)="updateValue($event)"
(focus)="modal.show()" (blur)="blur()" /> (focus)="focus()" (blur)="blur()" />
</ng-container> </ng-container>
<ng-template #circle> <ng-template #circle>
<div class="circle" [style.background]="snapshot.value" (click)="modal.show()"></div> <div class="circle" [style.background]="snapshot.value" (click)="focus()"></div>
</ng-template> </ng-template>
</span> </span>
<div *sqxModalView="modal" [sqxModalTarget]="input" position="bottom-left"> <div *sqxModalView="modal" [sqxModalTarget]="input" position="bottom-left"
[style.height]="'228px'"
[style.width]="'130px'">
<div [style.background]="snapshot.value" <div [style.background]="snapshot.value"
[cpToggle]="true" [cpToggle]="true"
[cpDialogDisplay]="'inline'" [cpDialogDisplay]="'inline'"
[cpCancelButton]="false" [cpCancelButton]="false"
[colorPicker]="snapshot.value" [colorPicker]="snapshot.value"
(colorPickerChange)="writeValue($event)"> (colorPickerChange)="updateValue($event)">
</div> </div>
</div> </div>

47
src/Squidex/app/framework/angular/forms/color-picker.component.ts

@ -32,19 +32,41 @@ interface State {
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class ColorPickerComponent extends StatefulControlComponent<State, string> { export class ColorPickerComponent extends StatefulControlComponent<State, string> {
private wasOpen = false;
@Input() @Input()
public placeholder = ''; public placeholder = '';
@Input() @Input()
public mode: 'Input' | 'Circle' = 'Input'; public mode: 'Input' | 'Circle' = 'Input';
@Input()
public set disabled(value: boolean) {
super.setDisabledState(value);
}
public modal = new ModalModel(); public modal = new ModalModel();
constructor(changeDetector: ChangeDetectorRef) { constructor(changeDetector: ChangeDetectorRef) {
super(changeDetector, { foreground: 'black' }); super(changeDetector, { foreground: 'black' });
this.modal.isOpen.subscribe(open => {
if (open) {
this.wasOpen = true;
} else {
if (this.wasOpen) {
this.callTouched();
}
this.wasOpen = false;
}
});
} }
public writeValue(obj: any) { public writeValue(obj: any) {
const previousColor = this.snapshot.value;
if (previousColor !== obj) {
let foreground = 'black'; let foreground = 'black';
if (MathHelper.toLuminance(MathHelper.parseColor(obj)!) < .5) { if (MathHelper.toLuminance(MathHelper.parseColor(obj)!) < .5) {
@ -52,11 +74,34 @@ export class ColorPickerComponent extends StatefulControlComponent<State, string
} }
this.next(s => ({ ...s, value: obj, foreground })); this.next(s => ({ ...s, value: obj, foreground }));
}
}
public focus() {
if (this.snapshot.isDisabled) {
return;
}
this.callChange(obj); this.modal.show();
} }
public blur() { public blur() {
if (this.snapshot.isDisabled) {
return;
}
this.callTouched(); this.callTouched();
} }
public updateValue(value: string) {
if (this.snapshot.isDisabled) {
return;
}
if (this.snapshot.value !== value) {
this.callChange(value);
this.writeValue(value);
}
}
} }

10
src/Squidex/app/framework/angular/forms/dropdown.component.html

@ -6,20 +6,20 @@
autocapitalize="off"> autocapitalize="off">
<div class="control-dropdown-item" *ngIf="snapshot.selectedItem"> <div class="control-dropdown-item" *ngIf="snapshot.selectedItem">
<ng-container *ngIf="!selectionTemplate">{{snapshot.selectedItem}}</ng-container> <ng-container *ngIf="!templateSelection">{{snapshot.selectedItem}}</ng-container>
<ng-template *ngIf="selectionTemplate" [sqxTemplateWrapper]="selectionTemplate" [item]="snapshot.selectedItem"></ng-template> <ng-template *ngIf="templateSelection" [sqxTemplateWrapper]="templateSelection" [item]="snapshot.selectedItem"></ng-template>
</div> </div>
<i class="icon-caret-down"></i> <i class="icon-caret-down"></i>
</div> </div>
<div class="items-container"> <div class="items-container">
<div class="control-dropdown" #container *sqxModalView="dropdown" [sqxModalTarget]="input" position="bottomLeft"> <div class="control-dropdown" #container *sqxModalView="dropdown" [sqxModalTarget]="input" position="bottom-left">
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === snapshot.selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === snapshot.selectedIndex" [container]="container"> <div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === snapshot.selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === snapshot.selectedIndex" [container]="container">
<ng-container *ngIf="!itemTemplate">{{item}}</ng-container> <ng-container *ngIf="!templateItem">{{item}}</ng-container>
<ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template> <ng-template *ngIf="templateItem" [sqxTemplateWrapper]="templateItem" [item]="item" [index]="i"></ng-template>
</div> </div>
</div> </div>
</div> </div>

7
src/Squidex/app/framework/angular/forms/dropdown.component.scss

@ -26,13 +26,18 @@ $color-input-disabled: #eef1f4;
.control-dropdown-item { .control-dropdown-item {
@include absolute(0, 1rem, 0, 0); @include absolute(0, 1rem, 0, 0);
pointer-events: none; pointer-events: none;
position: absolute;
line-height: 1.2rem;
} }
.icon-caret-down { .icon-caret-down {
@include absolute(30%, .4rem, auto, auto); @include absolute(30%, .4rem, auto, auto);
cursor: pointer;
font-size: .9rem; font-size: .9rem;
font-weight: normal; font-weight: normal;
pointer-events: none; pointer-events: none;
} }
.form-control {
cursor: default;
}
} }

15
src/Squidex/app/framework/angular/forms/dropdown.component.ts

@ -35,9 +35,8 @@ export class DropdownComponent extends StatefulControlComponent<State, any[]> im
public dropdown = new ModalModel(); public dropdown = new ModalModel();
public selectionTemplate: TemplateRef<any>; public templateSelection: TemplateRef<any>;
public templateItem: TemplateRef<any>;
public itemTemplate: TemplateRef<any>;
constructor(changeDetector: ChangeDetectorRef) { constructor(changeDetector: ChangeDetectorRef) {
super(changeDetector, { super(changeDetector, {
@ -48,16 +47,20 @@ export class DropdownComponent extends StatefulControlComponent<State, any[]> im
public ngAfterContentInit() { public ngAfterContentInit() {
if (this.templates.length === 1) { if (this.templates.length === 1) {
this.itemTemplate = this.selectionTemplate = this.templates.first; this.templateItem = this.templateSelection = this.templates.first;
} else { } else {
this.templates.forEach(template => { this.templates.forEach(template => {
if (template.name === 'selection') { if (template.name === 'selection') {
this.selectionTemplate = template; this.templateSelection = template;
} else { } else {
this.itemTemplate = template; this.templateItem = template;
} }
}); });
} }
if (this.templateItem) {
this.detectChanges();
}
} }
public writeValue(obj: any) { public writeValue(obj: any) {

24
src/Squidex/app/framework/angular/panel.component.ts

@ -89,17 +89,25 @@ export class PanelComponent implements AfterViewInit, OnDestroy, OnInit {
if (this.styleWidth !== size) { if (this.styleWidth !== size) {
this.styleWidth = size; this.styleWidth = size;
this.renderer.setStyle(this.panel.nativeElement, 'width', size); const element = this.panel ? this.panel.nativeElement : undefined;
this.renderer.setStyle(this.panel.nativeElement, 'minWidth', this.minWidth);
this.renderWidth = this.panel.nativeElement.offsetWidth; if (element) {
this.renderer.setStyle(element, 'width', size);
this.renderer.setStyle(element, 'minWidth', this.minWidth);
this.renderWidth = element.offsetWidth;
}
} }
} }
public arrange(left: any, layer: any) { public arrange(left: any, layer: any) {
this.renderer.setStyle(this.panel.nativeElement, 'top', '0px'); const element = this.panel ? this.panel.nativeElement : undefined;
this.renderer.setStyle(this.panel.nativeElement, 'left', left);
this.renderer.setStyle(this.panel.nativeElement, 'bottom', '0px'); if (element) {
this.renderer.setStyle(this.panel.nativeElement, 'position', 'absolute'); this.renderer.setStyle(element, 'top', '0px');
this.renderer.setStyle(this.panel.nativeElement, 'z-index', layer); this.renderer.setStyle(element, 'left', left);
this.renderer.setStyle(element, 'bottom', '0px');
this.renderer.setStyle(element, 'position', 'absolute');
this.renderer.setStyle(element, 'z-index', layer);
}
} }
} }

12
src/Squidex/app/shared/services/workflows.service.ts

@ -5,7 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Model, ResourceLinks } from '@app/framework'; import {
compareStringsAsc,
Model,
ResourceLinks
} from '@app/framework';
export class WorkflowDto extends Model<WorkflowDto> { export class WorkflowDto extends Model<WorkflowDto> {
public readonly _links: ResourceLinks; public readonly _links: ResourceLinks;
@ -20,8 +24,12 @@ export class WorkflowDto extends Model<WorkflowDto> {
this._links = links; this._links = links;
} }
public onCloned() {
this.steps.sort((a, b) => compareStringsAsc(a.name, b.name));
}
public getOpenSteps(step: WorkflowStep) { public getOpenSteps(step: WorkflowStep) {
return this.steps.filter(x => !this.transitions.find(y => y.from === step.name && y.to === x.name)); return this.steps.filter(x => x.name !== step.name && !this.transitions.find(y => y.from === step.name && y.to === x.name));
} }
public getTransitions(step: WorkflowStep): WorkflowTransitionView[] { public getTransitions(step: WorkflowStep): WorkflowTransitionView[] {

Loading…
Cancel
Save