diff --git a/AzureFunctions.AngularClient/src/app/binding-input/binding-input.component.ts b/AzureFunctions.AngularClient/src/app/binding-input/binding-input.component.ts
index bac061794d..aa53f78197 100644
--- a/AzureFunctions.AngularClient/src/app/binding-input/binding-input.component.ts
+++ b/AzureFunctions.AngularClient/src/app/binding-input/binding-input.component.ts
@@ -83,6 +83,9 @@ export class BindingInputComponent {
case ResourceType.ServiceBus:
this.pickerName = 'ServiceBus';
break;
+ case ResourceType.NotificationHub:
+ this.pickerName = 'NotificationHub';
+ break;
case ResourceType.AppSetting:
this.pickerName = 'AppSetting';
break;
@@ -113,7 +116,10 @@ export class BindingInputComponent {
const picker = this.input;
picker.inProcess = true;
- if (this.pickerName !== 'EventHub' && this.pickerName !== 'ServiceBus' && this.pickerName !== 'AppSetting') {
+ if (this.pickerName !== 'EventHub' &&
+ this.pickerName !== 'ServiceBus' &&
+ this.pickerName !== 'AppSetting' &&
+ this.pickerName !== 'NotificationHub') {
this._globalStateService.setBusyState(this._translateService.instant(PortalResources.resourceSelect));
diff --git a/AzureFunctions.AngularClient/src/app/binding/binding.component.html b/AzureFunctions.AngularClient/src/app/binding/binding.component.html
index 91e28c3b5b..528b981f73 100644
--- a/AzureFunctions.AngularClient/src/app/binding/binding.component.html
+++ b/AzureFunctions.AngularClient/src/app/binding/binding.component.html
@@ -75,7 +75,7 @@
{{ 'binding_createNewFunction' | translate }}
- {{ 'binding_go' | translate }}
+ {{ 'binding_go' | translate }}
{{ 'binding_go' | translate }}
diff --git a/AzureFunctions.AngularClient/src/app/binding/binding.component.scss b/AzureFunctions.AngularClient/src/app/binding/binding.component.scss
index 8929f24904..0ef9c42ffa 100644
--- a/AzureFunctions.AngularClient/src/app/binding/binding.component.scss
+++ b/AzureFunctions.AngularClient/src/app/binding/binding.component.scss
@@ -1,9 +1,13 @@
-@import '../../sass/main';
+@import '../../sass/common/variables';
.panel{
border: none;
}
+.panel, .panel-heading{
+ background-color: inherit;
+}
+
.binding-title{
margin-right: 10px;
font-size: 18px;
@@ -12,7 +16,6 @@
}
.panel-heading, .panel-body{
- background-color: #FFF;
border: none;
padding-left: 0px;
padding-right: 0px;
@@ -49,7 +52,6 @@
}
input, select{
- background: #fff;
position: relative;
vertical-align: middle;
height: 29px;
@@ -58,10 +60,6 @@ input, select{
padding: 0 5px;
}
-input:disabled{
- background-color: #F3F3F3;
-}
-
input[type=checkbox]{
height: 20px;
width: 20px;
@@ -83,12 +81,7 @@ i.select {
button.go {
margin-left: 100px;
}
-.button-go-disabled {
- cursor: not-allowed;
- background-color: $background-color-disabled;
- opacity: 0.4;
- color: black;
-}
+
.storage-creds-texbox {
float: left;
margin-right: 0px;
diff --git a/AzureFunctions.AngularClient/src/app/binding/binding.component.ts b/AzureFunctions.AngularClient/src/app/binding/binding.component.ts
index 356ef77a66..cb48e1d2f2 100644
--- a/AzureFunctions.AngularClient/src/app/binding/binding.component.ts
+++ b/AzureFunctions.AngularClient/src/app/binding/binding.component.ts
@@ -755,8 +755,8 @@ export class BindingComponent {
break;
case ResourceType.EventHub:
case ResourceType.ServiceBus:
+ case ResourceType.NotificationHub:
for (const key in this._appSettings) {
-
const value = this._appSettings[key].toLowerCase();
if (value.indexOf('sb://') > -1 && value.indexOf('sharedaccesskeyname') > -1) {
result.push(key);
diff --git a/AzureFunctions.AngularClient/src/app/busy-state/busy-state-scope-manager.ts b/AzureFunctions.AngularClient/src/app/busy-state/busy-state-scope-manager.ts
index d337f69495..a85f8f2b68 100644
--- a/AzureFunctions.AngularClient/src/app/busy-state/busy-state-scope-manager.ts
+++ b/AzureFunctions.AngularClient/src/app/busy-state/busy-state-scope-manager.ts
@@ -1,31 +1,32 @@
-import { BusyStateComponent } from './busy-state.component';
-import { Subscription as RxSubscription } from 'rxjs/Subscription';
+import { BroadcastEvent } from 'app/shared/models/broadcast-event';
+import { BusyStateEvent } from './../shared/models/broadcast-event';
+import { BroadcastService } from 'app/shared/services/broadcast.service';
+import { Guid } from './../shared/Utilities/Guid';
+import { BusyStateName } from './busy-state.component';
export class BusyStateScopeManager {
- private _busyState: BusyStateComponent;
private _busyStateKey: string | undefined;
- private _busyStateSubscription: RxSubscription;
- constructor(busyState: BusyStateComponent) {
- this._busyState = busyState;
- this._busyStateSubscription = this._busyState.clear.subscribe(() => this._busyStateKey = null);
+ constructor(
+ private _broadcastService: BroadcastService,
+ private _name: BusyStateName) {
+ this._busyStateKey = Guid.newGuid();
}
public setBusy() {
- this._busyStateKey = this._busyState.setScopedBusyState(this._busyStateKey);
+ this._broadcastService.broadcastEvent(BroadcastEvent.UpdateBusyState, {
+ busyComponentName: this._name,
+ action: 'setBusyState',
+ busyStateKey: this._busyStateKey
+ });
}
public clearBusy() {
- this._busyState.clearBusyState(this._busyStateKey);
- this._busyStateKey = null;
- }
-
- public dispose() {
- this.clearBusy();
- if (this._busyStateSubscription) {
- this._busyStateSubscription.unsubscribe();
- this._busyStateSubscription = null;
- }
+ this._broadcastService.broadcastEvent(BroadcastEvent.UpdateBusyState, {
+ busyComponentName: this._name,
+ action: 'clearBusyState',
+ busyStateKey: this._busyStateKey
+ });
}
}
diff --git a/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.html b/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.html
index 1f3089da82..d5afaf22cf 100644
--- a/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.html
+++ b/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.html
@@ -1,10 +1,4 @@
-
-
+
diff --git a/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.scss b/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.scss
index a87e573d2f..9fd5565f94 100644
--- a/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.scss
+++ b/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.scss
@@ -1,4 +1,4 @@
-@import '../../sass/main';
+@import '../../sass/common/variables';
.container {
height: 100%;
@@ -9,21 +9,25 @@
}
.try-functions-busy {
+ @extend .container;
background-color: #337ab7;
color: white;
font-family: "Segoe UI Light", "Segoe UI", "Segoe", Tahoma, Helvetica, Arial, sans-serif;
}
.global {
+ @extend .container;
position: fixed;
}
.busy-dashboard{
+ @extend .container;
width: calc(100% - #{$sidenav-width});
margin-left: $sidenav-width;
}
.busy-site-tabs{
+ @extend .container;
width: calc(100% - #{$sidenav-width});
height: calc(100% - #{$top-bar-height} - 1px);
}
@@ -56,8 +60,7 @@
left: 0;
width: 100%;
height: 100%;
- background-color: #dcdfe2;
- background-color: rgba(255, 255, 255, 0.30);
+ background-color: rgba($body-bg-color, 0.30);
z-index: 196;
}
.fxs-progress-dots {
@@ -92,5 +95,15 @@
.fxs-progress-dots > div:nth-child(4) {
padding-top: 20px;
width: 100%;
- background: white;
+ background-color: rgba($body-bg-color, 0.30);
+ }
+
+:host-context(#app-root[theme=dark]){
+ .fxs-progress{
+ background-color: rgba($body-bg-color-dark, 0.7);
+ }
+
+ .fxs-progress-dots > div:nth-child(4) {
+ background-color: rgba($body-bg-color-dark, 0.7);
+ }
}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.ts b/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.ts
index 4648217b0d..b5c1e8eec6 100644
--- a/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.ts
+++ b/AzureFunctions.AngularClient/src/app/busy-state/busy-state.component.ts
@@ -1,59 +1,104 @@
-import { Component, Input, Output, OnInit } from '@angular/core';
+import { LogCategories } from 'app/shared/models/constants';
+import { LogService } from './../shared/services/log.service';
+import { BusyStateEvent } from './../shared/models/broadcast-event';
+import { BroadcastEvent } from 'app/shared/models/broadcast-event';
+import { BroadcastService } from './../shared/services/broadcast.service';
+import { Component, Input, Output, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Guid } from './../shared/Utilities/Guid';
-import { BusyStateScopeManager } from './busy-state-scope-manager';
+
+export type BusyStateName =
+ 'global'
+ | 'dashboard'
+ | 'site-tabs'
+ | 'try-functions'
+ | 'function-keys';
@Component({
selector: 'busy-state',
templateUrl: './busy-state.component.html',
styleUrls: ['./busy-state.component.scss']
})
-export class BusyStateComponent implements OnInit {
+export class BusyStateComponent implements OnInit, OnDestroy {
public busy = false;
- @Input() name: string;
- isGlobal = false;
+ @Input() name: BusyStateName;
@Input() message: string;
@Output() clear = new Subject
();
+ @Input() cssClass: string;
+
+ private _ngUnsubscribe = new Subject();
- private busyStateMap: { [key: string]: boolean } = {};
- private reservedKey = '-';
+ private _busyStateMap: { [key: string]: boolean } = {};
+ private _busyStateDebounceMap: { [key: string]: Subject } = {};
+ private _reservedKey = '-';
+
+ constructor(
+ private _broadcastService: BroadcastService,
+ private _logService: LogService) {
+ }
ngOnInit() {
- this.isGlobal = this.name === 'global';
+
+ this._broadcastService.getEvents(BroadcastEvent.UpdateBusyState)
+ .takeUntil(this._ngUnsubscribe)
+ .filter(event => event.busyComponentName === this.name)
+ .subscribe(event => {
+
+ if (event.action === 'setBusyState') {
+ this._logService.verbose(LogCategories.busyState, `[${this.name}] Called set with key '${event.busyStateKey}'`)
+ } else if (event.action === 'clearBusyState') {
+ this._logService.verbose(LogCategories.busyState, `[${this.name}] Called clear with key '${event.busyStateKey}'`)
+ } else {
+ this._logService.verbose(LogCategories.busyState, `[${this.name}] Called clearOverall with key '${event.busyStateKey}'`)
+ }
+
+ this._debounceEventsBasedOnKey(event);
+ });
+ }
+
+ ngOnDestroy() {
+ this._ngUnsubscribe.next();
}
setBusyState() {
- this.setScopedBusyState(this.reservedKey);
+ this.setScopedBusyState(this._reservedKey);
}
setScopedBusyState(key: string): string {
key = key || Guid.newGuid();
- this.busyStateMap[key] = true;
+ this._busyStateMap[key] = true;
this.busy = true;
+
+ this._logService.debug(LogCategories.busyState, `[${this.name}][set] - Final state for key '${key}' is 'busy'`);
return key;
}
clearBusyState(key?: string) {
- key = key || this.reservedKey;
- if (this.busyStateMap[key]) {
- delete this.busyStateMap[key];
+ key = key || this._reservedKey;
+ if (this._busyStateMap[key]) {
+ delete this._busyStateMap[key];
}
- this.busy = !this.isEmptyMap(this.busyStateMap);
+ this.busy = !this.isEmptyMap(this._busyStateMap);
+ this._logService.debug(
+ LogCategories.busyState,
+ `[${this.name}][clear] - Final state for key '${key}' is '${this.busy ? 'busy' : 'not busy'}'`);
}
clearOverallBusyState() {
- this.busyStateMap = {};
+ this._busyStateMap = {};
this.clear.next(1);
this.busy = false;
+
+ this._logService.debug(LogCategories.busyState, `[${this.name}][clearOverall]`);
}
getBusyState(): boolean {
- return this.getScopedBusyState(this.reservedKey);
+ return this.getScopedBusyState(this._reservedKey);
}
getScopedBusyState(key: string): boolean {
- return !!key && !!this.busyStateMap[key];
+ return !!key && !!this._busyStateMap[key];
}
get isBusy(): boolean {
@@ -70,8 +115,31 @@ export class BusyStateComponent implements OnInit {
return true;
}
- getScopeManager(): BusyStateScopeManager {
- return new BusyStateScopeManager(this);
- }
+ // Debounce events based on key
+ private _debounceEventsBasedOnKey(event: BusyStateEvent) {
+ if (!this._busyStateDebounceMap[event.busyStateKey]) {
+ const keySubject = new Subject();
+ this._busyStateDebounceMap[event.busyStateKey] = keySubject;
+
+ keySubject
+ .takeUntil(this._ngUnsubscribe)
+ .debounceTime(50)
+ .subscribe(e => {
+ if (e.action === 'setBusyState') {
+ this.setScopedBusyState(e.busyStateKey);
+ } else if (e.action === 'clearBusyState') {
+ delete this._busyStateDebounceMap[e.busyStateKey];
+ this.clearBusyState(e.busyStateKey);
+ } else {
+ this.clearOverallBusyState();
+ this._busyStateDebounceMap = {};
+ }
+ });
+
+ keySubject.next(event);
+ } else {
+ this._busyStateDebounceMap[event.busyStateKey].next(event);
+ }
+ }
}
diff --git a/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.html b/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.html
index 8c3863b5ca..e0acd66ed0 100644
--- a/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.html
+++ b/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.html
@@ -1,13 +1,13 @@
-
-
+
+
{{control.value || ''}}
-
+
{{ 'hiddenValueClickToShow' | translate }}
-
diff --git a/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.scss b/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.scss
index cdf6ea3afd..602f57dcd5 100644
--- a/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.scss
+++ b/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.scss
@@ -1,4 +1,4 @@
-@import '../../../sass/main';
+@import '../../../sass/common/variables';
.click-to-edit-wrapper{
.hidden{
@@ -9,7 +9,7 @@
color: $disabled-color;
}
- .text{
+ .read-only-text{
overflow: hidden;
text-overflow: ellipsis;
padding: 3px;
diff --git a/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.ts b/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.ts
index 2c86b74b5a..1b5333ef9e 100644
--- a/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.ts
+++ b/AzureFunctions.AngularClient/src/app/controls/click-to-edit/click-to-edit.component.ts
@@ -1,7 +1,7 @@
import { DropDownComponent } from './../../drop-down/drop-down.component';
import { TextboxComponent } from './../textbox/textbox.component';
import { FormControl, FormGroup } from '@angular/forms';
-import { Component, OnInit, Input, OnDestroy, ContentChild } from '@angular/core';
+import { Component, ElementRef, OnInit, Input, AfterViewInit, OnDestroy, ContentChild, ViewChild } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
@@ -16,6 +16,8 @@ export class CustomFormGroup extends FormGroup {
// Overrides the ClickToEdit default behavior to start in edit mode for new items
public _msStartInEditMode: boolean;
+
+ public _msExistenceState: 'original' | 'new' | 'deleted' = 'original';
}
export class CustomFormControl extends FormControl {
@@ -28,7 +30,7 @@ export class CustomFormControl extends FormControl {
templateUrl: './click-to-edit.component.html',
styleUrls: ['./click-to-edit.component.scss'],
})
-export class ClickToEditComponent implements OnInit, OnDestroy {
+export class ClickToEditComponent implements OnInit, AfterViewInit, OnDestroy {
public showTextbox = false;
@Input() group: FormGroup;
@@ -36,34 +38,52 @@ export class ClickToEditComponent implements OnInit, OnDestroy {
@Input() placeholder: string;
@Input() hiddenText: boolean;
+ // This allows for a given control to affect the state of other controls in the group while not actually being "click-to-edit-able" itself.
+ // (i.e. The control's own editable/non-editable state is not affected by the extended fields in the CustomFormGroup its associated with.)
+ @Input() alwaysShow: boolean;
+
+ @ViewChild('target') target: ElementRef;
+
@ContentChild(TextboxComponent) textbox: TextboxComponent;
@ContentChild(DropDownComponent) dropdown: DropDownComponent
;
public control: CustomFormControl;
private _sub: Subscription;
+ private _targetFocusState: 'focused' | 'blurring' | 'blurred';
+ private _focusFunc = (e: FocusEvent) => { this._targetFocusListener(e); };
+ private _blurFunc = (e: FocusEvent) => { this._targetBlurListener(e); };
+
constructor() { }
ngOnInit() {
- this.control = this.group.controls[this.name];
+ this._targetFocusState = 'blurred';
- const group = this.group;
+ this.control = this.group.controls[this.name] as CustomFormControl;
+
+ const group = this.group as CustomFormGroup;
if (!group._msShowTextbox) {
group._msShowTextbox = new Subject();
}
this._sub = group._msShowTextbox.subscribe(showTextbox => {
- this.showTextbox = showTextbox;
+ this.showTextbox = showTextbox || this.alwaysShow || (group._msStartInEditMode && group.pristine);
+ if (this.showTextbox && (this.group as CustomFormGroup)._msFocusedControl === this.name) {
+ setTimeout(() => {
+ this._focusChild();
+ });
+ }
});
- if ((group)._msStartInEditMode) {
+ if (group._msStartInEditMode || this.alwaysShow) {
this.showTextbox = true;
}
+ }
- if (this.textbox) {
- this.textbox.blur.subscribe(() => this.onBlur());
- } else if (this.dropdown) {
- this.dropdown.blur.subscribe(() => this.onBlur());
+ ngAfterViewInit() {
+ if (this.target && this.target.nativeElement) {
+ this.target.nativeElement.addEventListener('focus', this._focusFunc, true);
+ this.target.nativeElement.addEventListener('blur', this._blurFunc, true);
}
}
@@ -72,23 +92,43 @@ export class ClickToEditComponent implements OnInit, OnDestroy {
this._sub.unsubscribe();
this._sub = null;
}
+ if (this.target && this.target.nativeElement) {
+ this.target.nativeElement.removeEventListener('focus', this._focusFunc, true);
+ this.target.nativeElement.removeEventListener('blur', this._blurFunc, true);
+ }
+ }
+
+ private _focusChild() {
+ if (this.textbox) {
+ this.textbox.focus();
+ } else if (this.dropdown) {
+ this.dropdown.focus();
+ } else {
+ return;
+ }
+
+ this._targetFocusState = 'focused';
}
- onClick() {
+ onMouseDown(event: MouseEvent) {
if (!this.showTextbox) {
- if (this.textbox) {
- this.textbox.focus();
- } else if (this.dropdown) {
- this.dropdown.focus();
- }
+ event.preventDefault();
+ this._updateShowTextbox(true);
}
+ }
+ private _onTargetFocus() {
this._updateShowTextbox(true);
}
- onBlur() {
- this.control._msRunValidation = true;
- this.control.updateValueAndValidity();
+ private _onTargetBlur() {
+ if (!this.group.pristine) {
+ for (let name in this.group.controls) {
+ const control = this.group.controls[name] as CustomFormControl;
+ control._msRunValidation = true;
+ control.updateValueAndValidity();
+ }
+ }
if (this.group.valid) {
@@ -100,12 +140,12 @@ export class ClickToEditComponent implements OnInit, OnDestroy {
// blur will remove the textbox and the click will never happen/
setTimeout(() => {
this._updateShowTextbox(false);
- }, 100);
+ }, 0);
}
}
protected _updateShowTextbox(show: boolean) {
- const group = this.group;
+ const group = this.group as CustomFormGroup;
if (show) {
group._msFocusedControl = this.name;
@@ -117,4 +157,22 @@ export class ClickToEditComponent implements OnInit, OnDestroy {
group._msShowTextbox.next(show);
}
}
+
+ private _targetBlurListener(event: FocusEvent) {
+ this._targetFocusState = 'blurring';
+ setTimeout(() => {
+ if (this._targetFocusState !== 'focused') {
+ this._targetFocusState = 'blurred';
+ this._onTargetBlur();
+ }
+ });
+ }
+
+ private _targetFocusListener(event: FocusEvent) {
+ if (this._targetFocusState === 'blurred') {
+ this._onTargetFocus();
+ }
+ this._targetFocusState = 'focused';
+ }
+
}
diff --git a/AzureFunctions.AngularClient/src/app/controls/command-bar/command/command.component.html b/AzureFunctions.AngularClient/src/app/controls/command-bar/command/command.component.html
index 5a1814036f..d0b6aab6b4 100644
--- a/AzureFunctions.AngularClient/src/app/controls/command-bar/command/command.component.html
+++ b/AzureFunctions.AngularClient/src/app/controls/command-bar/command/command.component.html
@@ -1,3 +1,4 @@
-
- {{ displayText }}
+
+
+ {{displayText}}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.html b/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.html
index 0036a69a11..6f5801d44e 100644
--- a/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.html
+++ b/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.html
@@ -18,14 +18,14 @@
-
+
-
+
{{addButtonLabel}}
diff --git a/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.scss b/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.scss
index 85493afa2f..8acf0b44d4 100644
--- a/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.scss
+++ b/AzureFunctions.AngularClient/src/app/controls/pair-list/pair-list.component.scss
@@ -8,17 +8,6 @@
flex-basis: 170px;
}
-.name-fixed {
- align-self: baseline;
- margin-right: 14px;
- overflow: auto;
- border: 1px solid #dedede;
- line-height: 25px;
- flex-basis: 170px;
- padding-left: 5px;
- background-color: #f5f5f5;
-}
-
.value {
flex-grow:1;
}
@@ -28,8 +17,6 @@
}
input, select{
- background: #fff;
- border: 1px solid #dedede;
position: relative;
vertical-align: middle;
height: 29px;
diff --git a/AzureFunctions.AngularClient/src/app/controls/slide-toggle/slide-toggle.component.scss b/AzureFunctions.AngularClient/src/app/controls/slide-toggle/slide-toggle.component.scss
index 6a6318f72c..bc476846bd 100644
--- a/AzureFunctions.AngularClient/src/app/controls/slide-toggle/slide-toggle.component.scss
+++ b/AzureFunctions.AngularClient/src/app/controls/slide-toggle/slide-toggle.component.scss
@@ -1,4 +1,4 @@
-@import '../../../sass/main';
+@import '../../../sass/common/variables';
/*Overwrite bootstrap*/
a.toggle-container {
diff --git a/AzureFunctions.AngularClient/src/app/tab/tab.component.html b/AzureFunctions.AngularClient/src/app/controls/tabs/tab/tab.component.html
similarity index 100%
rename from AzureFunctions.AngularClient/src/app/tab/tab.component.html
rename to AzureFunctions.AngularClient/src/app/controls/tabs/tab/tab.component.html
diff --git a/AzureFunctions.AngularClient/src/app/tab/tab.component.spec.ts b/AzureFunctions.AngularClient/src/app/controls/tabs/tab/tab.component.spec.ts
similarity index 100%
rename from AzureFunctions.AngularClient/src/app/tab/tab.component.spec.ts
rename to AzureFunctions.AngularClient/src/app/controls/tabs/tab/tab.component.spec.ts
diff --git a/AzureFunctions.AngularClient/src/app/controls/tabs/tab/tab.component.ts b/AzureFunctions.AngularClient/src/app/controls/tabs/tab/tab.component.ts
new file mode 100644
index 0000000000..c7bf7451ee
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/controls/tabs/tab/tab.component.ts
@@ -0,0 +1,11 @@
+import { Input, Component } from '@angular/core';
+
+@Component({
+ selector: 'tab',
+ templateUrl: './tab.component.html'
+})
+export class TabComponent {
+ @Input() title: string;
+ @Input() id: string;
+ @Input() active = false;
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.html b/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.html
new file mode 100644
index 0000000000..4716d173c1
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.html
@@ -0,0 +1,17 @@
+
+
+
+
+ {{tab.title}}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.scss b/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.scss
new file mode 100644
index 0000000000..ab42b8f3ce
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.scss
@@ -0,0 +1,40 @@
+@import '../../../sass/common/variables';
+
+ul.tabs-row{
+ box-sizing: border-box;
+ list-style-type: none;
+ padding-left: 0px;
+ margin: 0px;
+ border-bottom: $border;
+}
+
+li.tab-cell{
+ box-sizing: border-box;
+ display: inline-block;
+ border-top-width: 0px;
+ text-align: center;
+ padding: 8px 25px;
+ min-width: 150px;
+ outline: none;
+ margin-left: 0px;
+ position: relative;
+
+ .bottom{
+ position: absolute;
+ bottom: -1px;
+ background-color: $body-bg-color;
+ height: 1px;
+ width: 100%;
+ left: 0px;
+ }
+}
+
+#app-root[theme=dark]{
+ .tab-label-selected{
+ color: $default-text-color-dark;
+ }
+
+ li.tab-label{
+ color: $primary-color-dark;
+ }
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.ts b/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.ts
new file mode 100644
index 0000000000..830d25e382
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/controls/tabs/tabs.component.ts
@@ -0,0 +1,44 @@
+import { ContentChildren, QueryList, AfterContentInit, Component, ViewEncapsulation } from '@angular/core';
+import { TabComponent } from 'app/controls/tabs/tab/tab.component';
+
+@Component({
+ selector: 'tabs',
+ templateUrl: './tabs.component.html',
+ styleUrls: ['./tabs.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class TabsComponent implements AfterContentInit {
+
+ @ContentChildren(TabComponent) tabs: QueryList
;
+
+ constructor() {
+ }
+
+ ngAfterContentInit() {
+ const activeTabs = this.tabs.filter((tab) => tab.active);
+
+ if (activeTabs.length === 0) {
+ this.selectTabHelper(this.tabs.first);
+ }
+ }
+
+ selectTabId(tabId: string) {
+ const tabs = this.tabs.toArray();
+ const tab = tabs.find(t => t.id === tabId);
+ if (tab) {
+ this.selectTab(tab);
+ }
+ }
+
+ selectTab(tab: TabComponent) {
+ this.selectTabHelper(tab);
+ }
+
+ selectTabHelper(tab: TabComponent) {
+
+ this.tabs.toArray().forEach(t => t.active = false);
+ if (tab) {
+ tab.active = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.html b/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.html
index e079f92620..bc7a976b1c 100644
--- a/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.html
+++ b/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.html
@@ -10,7 +10,7 @@
type="text"
[formControl]="control"
[placeholder]="placeholder"
- (blur)="onBlur($event)" />
+ [class.dirty]="highlightDirty && control?.dirty" />
0"
diff --git a/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.scss b/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.scss
index 76083d34ce..15bd85b673 100644
--- a/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.scss
+++ b/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.scss
@@ -1,4 +1,4 @@
-@import '../../../sass/main';
+@import '../../../sass/common/variables';
.textbox-wrapper{
width: 100%;
@@ -7,6 +7,10 @@
input{
height: 23px;
width: 100%;
+
+ &.dirty{
+ border-color: $alt1-color;
+ }
}
.validation-error{
diff --git a/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.ts b/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.ts
index 7ff0d8002e..a27e1cebc2 100644
--- a/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.ts
+++ b/AzureFunctions.AngularClient/src/app/controls/textbox/textbox.component.ts
@@ -1,6 +1,5 @@
import { FormControl } from '@angular/forms';
-import { Component, OnInit, Input, Output, ViewChild } from '@angular/core';
-import { Subject } from 'rxjs/Subject';
+import { Component, OnInit, Input, ViewChild } from '@angular/core';
@Component({
selector: 'textbox',
@@ -10,9 +9,8 @@ import { Subject } from 'rxjs/Subject';
export class TextboxComponent implements OnInit {
@Input() control: FormControl;
- @Input() placeholder: string;
-
- @Output() blur = new Subject();
+ @Input() placeholder = '';
+ @Input() highlightDirty: boolean;
@ViewChild('textboxInput') textboxInput: any;
@@ -24,15 +22,9 @@ export class TextboxComponent implements OnInit {
ngOnInit() {
}
- onBlur(event: any) {
- this.blur.next(event);
- }
-
focus() {
if (this.textboxInput) {
- setTimeout(() => {
- this.textboxInput.nativeElement.focus();
- })
+ this.textboxInput.nativeElement.focus();
}
}
}
diff --git a/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.html b/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.html
index f2b03377ac..3edd2f653f 100644
--- a/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.html
+++ b/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.html
@@ -6,7 +6,7 @@
- {{ 'copypre_copy' | translate }}
+ {{ 'copypre_copy' | translate }}
diff --git a/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.scss b/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.scss
index 98310e1195..4616f95ec0 100644
--- a/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.scss
+++ b/AzureFunctions.AngularClient/src/app/copy-pre/copy-pre.component.scss
@@ -1,4 +1,4 @@
-@import '../../sass/main';
+@import '../../sass/common/variables';
.wrapper {
display: flex;
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.html
new file mode 100644
index 0000000000..86409ab409
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.html
@@ -0,0 +1,27 @@
+
+ Buttons
+
+
+
+
+ <!-- Standard button -->
+ <button class="custom-button">custom-button</button>
+
+ <!-- Inverted button -->
+ <button class="custom-button-invert">custom-button-invert</button>
+
+ <!-- Standard disabled button -->
+ <button class="custom-button" disabled>custom-button disabled</button>
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.scss
new file mode 100644
index 0000000000..d3ebd1df66
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.scss
@@ -0,0 +1,5 @@
+
+.custom-button, .custom-button-invert{
+ margin-left: 0px;
+ margin-right: 5px;
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.ts
new file mode 100644
index 0000000000..f42880101b
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/button-example/button-example.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'button-example',
+ styleUrls: ['./button-example.component.scss'],
+ templateUrl: './button-example.component.html'
+})
+export class ButtonExampleComponent {
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.html
new file mode 100644
index 0000000000..8292a6871f
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.html
@@ -0,0 +1,64 @@
+
+ Colors
+ Here's some general rules to help you deal with colors properly:
+
+
+
+ DON'T HARD-CODE COLORS! - You should always reference colors defined in the _variables.scss.
+ If you need a different shade of a color, you can use mixins like "lighten()" or "darken()" instead of defining a new one.
+
+
+ Try to avoid defining a new color rule, but if you must, make sure you redefine it for every theme -
+ More colors means more work!
+
+
+ Prefer to make color styles at a global level that can be reused - Defining general purpose styles
+ that can be reused globally makes it easier to add themeing later because you only need to make the change in one place. For
+ example, instead of writing a component-level class called ".my-component-link" to handle link color and hover behavior, you can just
+ reference a globally defined "link" class in your HTML, or better yet, switch to an anchor tag which already handles theme rules in a single place.
+
+
+
+
+
+ Theming for Global Styles
+ Add this to your global-level style to handle different themes:
+
+
+
+ // Normal style
+ .myclass{{ '{' }}
+ color: $default-text-color;
+ {{ '}' }}
+
+ // Dark theme version
+ #app-root[theme=dark]{{ '{' }}
+ .myclass{{ '{' }}
+ color: $default-text-color-dark;
+ {{ '}' }}
+ {{ '}' }}
+
+
+
+
+
+ Theming for Component Styles
+ Add this to your component-level styles to handle different themes. Since components are protected
+ by view encapsulation, you need to use a ":host-context" selector to select external elements with CSS
+
+
+
+ // Normal style
+ .myclass{{ '{' }}
+ color: $default-text-color;
+ {{ '}' }}
+
+ // Dark theme version
+ :host-context(#app-root[theme=dark]){{ '{' }}
+ .myclass{{ '{' }}
+ color: $default-text-color-dark;
+ {{ '}' }}
+ {{ '}' }}
+
+
+
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.scss
new file mode 100644
index 0000000000..34b10cb93b
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.scss
@@ -0,0 +1,12 @@
+ol{
+ margin-top: 15px;
+
+ li{
+ margin-top: 10px;
+ line-height: 20px;
+ }
+}
+
+article{
+ margin-top: 30px;
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.ts
new file mode 100644
index 0000000000..f2d60626b5
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/color-example/color-example.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'color-example',
+ styleUrls: ['./color-example.component.scss'],
+ templateUrl: './color-example.component.html'
+})
+export class ColorExampleComponent {
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.html
new file mode 100644
index 0000000000..78315953c7
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.scss
new file mode 100644
index 0000000000..d975d07691
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.scss
@@ -0,0 +1,28 @@
+@import '../../sass/common/variables';
+
+#dev-guide{
+ padding: 0px 40px;
+ max-width: 1200px;
+
+ label{
+ display: block;
+ }
+}
+
+.example{
+ margin-top: 30px;
+ margin-bottom: 60px;
+
+ .header{
+ border: $border;
+ padding: 10px;
+ }
+}
+
+figure > label{
+ margin-top: 10px;
+}
+
+.tab-label, .tab-label-selected{
+ font-size: 17px;
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.ts
new file mode 100644
index 0000000000..bc4689cd28
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.component.ts
@@ -0,0 +1,10 @@
+import { Component, ViewEncapsulation } from '@angular/core';
+
+@Component({
+ selector: 'dev-guide',
+ templateUrl: './dev-guide.component.html',
+ styleUrls: ['./dev-guide.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class DevGuideComponent{
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.module.ts b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.module.ts
new file mode 100644
index 0000000000..4631d1718c
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/dev-guide.module.ts
@@ -0,0 +1,46 @@
+import { EditableTblExampleComponent } from './editable-tbl-example/editable-tbl-example.component';
+import { TblExampleComponent } from './tbl-example/tbl-example.component';
+import { RadioSelectorExampleComponent } from './radio-selector-example/radio-selector-example.component';
+import { TextboxExampleComponent } from './textbox-example/textbox-example.component';
+import { TabComponent } from './../controls/tabs/tab/tab.component';
+import { TabsComponent } from './../controls/tabs/tabs.component';
+import { SvgExampleComponent } from './svg-example/svg-example.component';
+import { ColorExampleComponent } from './color-example/color-example.component';
+import { ButtonExampleComponent } from './button-example/button-example.component';
+import { ListExampleComponent } from './list-example/list-example.component';
+import { TypographyExampleComponent } from './typography-example/typography-example.component';
+import { DevGuideComponent } from './dev-guide.component';
+import { RouterModule } from '@angular/router';
+import { TranslateModule } from '@ngx-translate/core';
+import { SharedModule } from './../shared/shared.module';
+import { NgModule, ModuleWithProviders } from '@angular/core';
+
+const routing: ModuleWithProviders = RouterModule.forChild([
+ {
+ path: '', component: DevGuideComponent
+ }
+]);
+
+@NgModule({
+ imports: [
+ TranslateModule.forChild(),
+ SharedModule,
+ routing
+ ],
+ declarations: [
+ TabsComponent,
+ TabComponent,
+ DevGuideComponent,
+ TypographyExampleComponent,
+ ListExampleComponent,
+ ButtonExampleComponent,
+ ColorExampleComponent,
+ SvgExampleComponent,
+ TextboxExampleComponent,
+ RadioSelectorExampleComponent,
+ TblExampleComponent,
+ EditableTblExampleComponent
+ ],
+ providers: []
+})
+export class DevGuideModule { }
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.html
new file mode 100644
index 0000000000..9ff14817b0
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.html
@@ -0,0 +1,73 @@
+
+ Click-to-Edit Table Example
+
+
+ HTML
+
+
+ <tbl [items]="groupArray.controls" #table="tbl" name="Editable table">
+ <tr><th colspan="2"></th></tr>
+ <tr *ngFor="let group of table.items">
+ <td>
+ <click-to-edit [group]="group" name="name">
+ <textbox [control]="group.controls['name']"></textbox>
+ </click-to-edit>
+ </td>
+ <td>
+ <click-to-edit [group]="group" name="value">
+ <textbox [control]="group.controls['value']"></textbox>
+ </click-to-edit>
+ </td>
+ </tr>
+ </tbl>
+
+
+ TypeScript
+
+
+ export class EditableTblExampleComponent {{ '{' }}
+ @ViewChild('table') table: TblComponent;
+
+ public groupArray: FormArray;
+
+ constructor(private _fb: FormBuilder, private _translateService: TranslateService) {{ '{' }}
+ this.groupArray = this._fb.array([]);
+ const requiredValidator = new RequiredValidator(this._translateService);
+
+ this.groupArray.push(this._fb.group({{ '{' }}
+ name: ['a', requiredValidator.validate.bind(requiredValidator)],
+ value: ['1', requiredValidator.validate.bind(requiredValidator)]
+ {{ '}' }}));
+
+ this.groupArray.push(this._fb.group({{ '{' }}
+ name: ['b', requiredValidator.validate.bind(requiredValidator)],
+ value: ['2', requiredValidator.validate.bind(requiredValidator)]
+ {{ '}' }}));
+
+ this.groupArray.push(this._fb.group({{ '{' }}
+ name: ['c', requiredValidator.validate.bind(requiredValidator)],
+ value: ['3', requiredValidator.validate.bind(requiredValidator)]
+ {{ '}' }}));
+ {{ '}' }}
+ {{ '}' }}
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.scss
new file mode 100644
index 0000000000..363c58f121
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.scss
@@ -0,0 +1,9 @@
+tr{
+ >th{
+ height: 5px;
+ }
+
+ >td{
+ width: 200px;
+ }
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.ts
new file mode 100644
index 0000000000..643ef1b7ff
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/editable-tbl-example/editable-tbl-example.component.ts
@@ -0,0 +1,36 @@
+import { TranslateService } from '@ngx-translate/core';
+import { FormArray, FormBuilder } from '@angular/forms';
+import { TblComponent } from './../../controls/tbl/tbl.component';
+import { Component, ViewChild } from '@angular/core';
+import { RequiredValidator } from 'app/shared/validators/requiredValidator';
+
+@Component({
+ selector: 'editable-tbl-example',
+ styleUrls: ['./editable-tbl-example.component.scss'],
+ templateUrl: './editable-tbl-example.component.html'
+})
+export class EditableTblExampleComponent {
+ @ViewChild('table') table: TblComponent;
+
+ public groupArray: FormArray;
+
+ constructor(private _fb: FormBuilder, private _translateService: TranslateService) {
+ this.groupArray = this._fb.array([]);
+ const requiredValidator = new RequiredValidator(this._translateService);
+
+ this.groupArray.push(this._fb.group({
+ name: ['a', requiredValidator.validate.bind(requiredValidator)],
+ value: ['1', requiredValidator.validate.bind(requiredValidator)]
+ }));
+
+ this.groupArray.push(this._fb.group({
+ name: ['b', requiredValidator.validate.bind(requiredValidator)],
+ value: ['2', requiredValidator.validate.bind(requiredValidator)]
+ }));
+
+ this.groupArray.push(this._fb.group({
+ name: ['c', requiredValidator.validate.bind(requiredValidator)],
+ value: ['3', requiredValidator.validate.bind(requiredValidator)]
+ }));
+ }
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.html
new file mode 100644
index 0000000000..a919b90587
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.html
@@ -0,0 +1,26 @@
+
+ Lists
+
+ Adding a "list-item" or "list-item selected" class will give you common hover and selection coloring. We could probably
+ make the base styles for this more complex (like add padding, borders, etc...), but we'd have to
+ update a lot of code that's currently using this class first.
+
+
+
+
+
+ <div class="list-item">Item 1</div>
+ <div class="list-item">Item 2</div>
+ <div class="list-item">Item 3</div>
+ <div class="list-item selected">Item 4 selected</div>
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.scss
new file mode 100644
index 0000000000..269e20a18c
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.scss
@@ -0,0 +1,11 @@
+
+@import '../../../sass/common/variables';
+
+.list-item{
+ width: 400px;
+}
+
+.header > div{
+ border: $border;
+ width: 402px;
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.ts
new file mode 100644
index 0000000000..1965ccda52
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/list-example/list-example.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'list-example',
+ styleUrls: ['./list-example.component.scss'],
+ templateUrl: './list-example.component.html'
+})
+export class ListExampleComponent {
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.html
new file mode 100644
index 0000000000..8f272886e2
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.html
@@ -0,0 +1,34 @@
+
+ Radio Selector
+
+
+ HTML
+
+
+ <radio-selector [control]="control" [options]="options" [highlightDirty]="true"></radio-selector>
+
+
+ TypeScript
+
+
+ export class RadioSelectorExampleComponent {{ '{' }}
+ control: FormControl;
+ options: SelectOption<'off' | 'on'>[] = [{{ '{' }}
+ displayLabel: 'Off',
+ value: 'off'
+ {{ '}' }},
+ {{ '{' }}
+ displayLabel: 'On',
+ value: 'on'
+ {{ '}' }}];
+
+ constructor(fb: FormBuilder) {{ '{' }}
+ this.control = fb.control('off', null);
+ {{ '}' }}
+ {{ '}' }}
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/site/site-config/general-settings/general-settings.component.spec.ts b/AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.scss
similarity index 100%
rename from AzureFunctions.AngularClient/src/app/site/site-config/general-settings/general-settings.component.spec.ts
rename to AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.scss
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.ts
new file mode 100644
index 0000000000..e7b858ac9c
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/radio-selector-example/radio-selector-example.component.ts
@@ -0,0 +1,24 @@
+import { SelectOption } from './../../shared/models/select-option';
+import { FormControl, FormBuilder } from '@angular/forms';
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'radio-selector-example',
+ styleUrls: ['./radio-selector-example.component.scss'],
+ templateUrl: './radio-selector-example.component.html'
+})
+export class RadioSelectorExampleComponent {
+ control: FormControl;
+ options: SelectOption<'off' | 'on'>[] = [{
+ displayLabel: 'Off',
+ value: 'off'
+ },
+ {
+ displayLabel: 'On',
+ value: 'on'
+ }];
+
+ constructor(fb: FormBuilder) {
+ this.control = fb.control('off', null);
+ }
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.html
new file mode 100644
index 0000000000..d33287cfe8
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.html
@@ -0,0 +1,32 @@
+
+ SVG's (Notice that icon colors update with theme changes)
+
+
+
+
+ <label>Small</label>
+ <span load-image="image/delete.svg" class="icon-small"></span>
+
+ <label>Medium</label>
+ <span load-image="image/delete.svg" class="icon-medium"></span>
+
+ <Label>Large</Label>
+ <span load-image="image/delete.svg" class="icon-large"></span>
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.scss
new file mode 100644
index 0000000000..f57b6f2da6
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.scss
@@ -0,0 +1,3 @@
+#poly-icon{
+ margin-top: 20px;
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.ts
new file mode 100644
index 0000000000..63825a25ea
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/svg-example/svg-example.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'svg-example',
+ styleUrls: ['./svg-example.component.scss'],
+ templateUrl: './svg-example.component.html'
+})
+export class SvgExampleComponent {
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.html
new file mode 100644
index 0000000000..2d23b46eb9
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.html
@@ -0,0 +1,58 @@
+
+ Simple Sortable Table Example
+
+
+ HTML
+
+
+ <tbl [items]="items" #table="tbl" name="Simple table">
+ <tr>
+ <th><tbl-th name="name">Sortable header</tbl-th></th>
+ <th>Non-sortable header</th>
+ </tr>
+
+ <tr *ngFor="let item of table.items">
+ <td>{{ '{' }}{{ '{' }}item.name{{ '}' }}{{ '}' }}</td>
+ <td>{{ '{' }}{{ '{' }}item.value{{ '}' }}{{ '}' }}</td>
+ </tr>
+ </tbl>
+
+
+ TypeScript
+
+
+ export class TblExampleComponent {{ '{' }}
+ @ViewChild('table') table: TblComponent;
+
+ items: {{ '{' }} name: string, value: string }[];
+
+ constructor() {{ '{' }}
+ this.items = [{{ '{' }}
+ name: 'a',
+ value: '1'
+ {{ '}' }},
+ {{ '{' }}
+ name: 'b',
+ value: '2'
+ {{ '}' }},
+ {{ '{' }}
+ name: 'c',
+ value: '3'
+ {{ '}' }}];
+ {{ '}' }}
+ {{ '}' }}
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.ts
new file mode 100644
index 0000000000..ebb35aed24
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/tbl-example/tbl-example.component.ts
@@ -0,0 +1,28 @@
+import { TblComponent } from './../../controls/tbl/tbl.component';
+import { Component, ViewChild } from '@angular/core';
+
+@Component({
+ selector: 'tbl-example',
+ styleUrls: ['./tbl-example.component.scss'],
+ templateUrl: './tbl-example.component.html'
+})
+export class TblExampleComponent {
+ @ViewChild('table') table: TblComponent;
+
+ items: { name: string, value: string }[];
+
+ constructor() {
+ this.items = [{
+ name: 'a',
+ value: '1'
+ },
+ {
+ name: 'b',
+ value: '2'
+ },
+ {
+ name: 'c',
+ value: '3'
+ }];
+ }
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.html
new file mode 100644
index 0000000000..45b8b1dfff
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.html
@@ -0,0 +1,29 @@
+
+ Textbox
+
+
+ HTML
+
+
+ <textbox placeholder="Enter some text and then and delete it" [control]="control"></textbox>
+
+
+ TypeScript
+
+
+ export class TextboxExampleComponent {{ '{' }}
+ control: FormControl;
+
+ constructor(fb: FormBuilder, translateService: TranslateService){{ '{' }}
+ const required = new RequiredValidator(translateService);
+ this.control = fb.control('', required.validate.bind(required));
+ {{ '}' }}
+ {{ '}' }}
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.scss
new file mode 100644
index 0000000000..1bbf8f406c
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.scss
@@ -0,0 +1,3 @@
+.textbox-container{
+ max-width: 500px;
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.ts
new file mode 100644
index 0000000000..fe540027ca
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/textbox-example/textbox-example.component.ts
@@ -0,0 +1,18 @@
+import { TranslateService } from '@ngx-translate/core';
+import { FormControl, FormBuilder } from '@angular/forms';
+import { Component } from '@angular/core';
+import { RequiredValidator } from 'app/shared/validators/requiredValidator';
+
+@Component({
+ selector: 'textbox-example',
+ styleUrls: ['./textbox-example.component.scss'],
+ templateUrl: './textbox-example.component.html'
+})
+export class TextboxExampleComponent {
+ control: FormControl;
+
+ constructor(fb: FormBuilder, translateService: TranslateService){
+ const required = new RequiredValidator(translateService);
+ this.control = fb.control('', required.validate.bind(required));
+ }
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.html b/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.html
new file mode 100644
index 0000000000..1600fda67c
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.html
@@ -0,0 +1,25 @@
+
+ Typography
+
+
+
+
+ <h1>Heading 1</h1>
+ <h2>Heading 2</h2>
+ <h3>Heading 3</h3>
+ <h4>Heading 4</h4>
+ <label>Label</label>
+ <a>Link</a>
+ Regular text
+
+
+
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.scss b/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.scss
new file mode 100644
index 0000000000..782d9c0724
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.scss
@@ -0,0 +1,9 @@
+#typography-example{
+ h1,h2,h3,h4,label,a{
+ margin-bottom: 10px;
+ }
+
+ a{
+ display: block;
+ }
+}
diff --git a/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.ts b/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.ts
new file mode 100644
index 0000000000..60cb6eb395
--- /dev/null
+++ b/AzureFunctions.AngularClient/src/app/dev-guide/typography-example/typography-example.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'typography-example',
+ styleUrls: ['./typography-example.component.scss'],
+ templateUrl: './typography-example.component.html'
+})
+export class TypographyExampleComponent {
+}
diff --git a/AzureFunctions.AngularClient/src/app/disabled-dashboard/disabled-dashboard.component.scss b/AzureFunctions.AngularClient/src/app/disabled-dashboard/disabled-dashboard.component.scss
index c28275a26d..6e7a2fbdc7 100644
--- a/AzureFunctions.AngularClient/src/app/disabled-dashboard/disabled-dashboard.component.scss
+++ b/AzureFunctions.AngularClient/src/app/disabled-dashboard/disabled-dashboard.component.scss
@@ -1,4 +1,4 @@
-@import '../../sass/main';
+@import '../../sass/common/variables';
.disabled-container{
position: absolute;
diff --git a/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.html b/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.html
index 485b1683bb..1710993e11 100644
--- a/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.html
+++ b/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.html
@@ -2,8 +2,8 @@
diff --git a/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.scss b/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.scss
index fe174be7fa..e0bd5a24ea 100644
--- a/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.scss
+++ b/AzureFunctions.AngularClient/src/app/download-function-app-content/download-function-app-content.component.scss
@@ -1,3 +1,7 @@
+h3{
+ display: inline;
+}
+
#download-button-container {
align-self: center;
margin-top: 30px;
diff --git a/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.html b/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.html
index 9bdb802ebb..94688faa31 100644
--- a/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.html
+++ b/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.html
@@ -2,7 +2,8 @@
*ngIf="_options"
class="drop-down-container"
[class.dirty]="highlightDirty && control?.dirty"
- [class.disabled]="control ? control.disabled : disabled">
+ [class.disabled]="control ? control.disabled : disabled"
+ [class.small]="size === 'small'">
- {{option.displayLabel}}
+
+
+ {{option.displayLabel}}
+
+
+ {{option.displayLabel}}
@@ -24,11 +30,15 @@
(change)="onSelectValue($event.target.value)"
class="drop-down-select"
[formControl]="control"
- (blur)="onBlur($event)"
#selectInput>
-
{{option.displayLabel}}
+
+
+ {{option.displayLabel}}
+
+
+
{{option.displayLabel}}
diff --git a/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.scss b/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.scss
index 688cf73f48..3c6344a72e 100644
--- a/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.scss
+++ b/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.scss
@@ -1,7 +1,6 @@
-@import '../../sass/main';
+@import '../../sass/common/variables';
.drop-down-container {
- background: #fff;
border: 1px solid #dedede;
padding-right: 30px;
position: relative;
@@ -76,4 +75,25 @@
-ms-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
+}
+
+.small {
+ .drop-down-select {
+ height: 23px;
+ }
+
+ .drop-down-selected-value {
+ height: 21px;
+ line-height: 21px;
+ font-size: 12px;
+ }
+
+ .drop-down-selected-arrow {
+ height: 21px;
+ width: 21px;
+ }
+
+ select {
+ font-size: 12px;
+ }
}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.ts b/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.ts
index 6d95064974..dbcdfae759 100644
--- a/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.ts
+++ b/AzureFunctions.AngularClient/src/app/drop-down/drop-down.component.ts
@@ -1,7 +1,6 @@
-import { Subject } from 'rxjs/Subject';
import { FormGroup, FormControl } from '@angular/forms';
import { Component, OnInit, OnChanges, SimpleChanges, EventEmitter, ViewChild, Input, Output } from '@angular/core';
-import { DropDownElement } from '../shared/models/drop-down-element';
+import { DropDownElement, DropDownGroupElement } from '../shared/models/drop-down-element';
@Component({
@@ -17,13 +16,14 @@ export class DropDownComponent
implements OnInit, OnChanges {
@Input() placeholder: string;
@Input() disabled: boolean;
@Input() highlightDirty: boolean;
+ @Input() size: null | 'small' | 'large';
@Output() value: EventEmitter;
- @Output() blur = new Subject();
public selectedElement: DropDownElement;
public empty: any;
- public _options: DropDownElement[];
+ public _options: DropDownGroupElement[] | DropDownElement[];
+ public optionsGrouped: boolean;
@ViewChild('selectInput') selectInput: any;
@@ -47,32 +47,83 @@ export class DropDownComponent implements OnInit, OnChanges {
}
}
- @Input() set options(value: DropDownElement[]) {
+ @Input() set options(value: DropDownGroupElement[] | DropDownElement[]) {
this._options = [];
- for (let i = 0; i < value.length; i++) {
- this._options.push({
- id: i,
- displayLabel: value[i].displayLabel,
- value: value[i].value,
- default: value[i].default
+
+ if (!value || value.length === 0) {
+ delete this.selectedElement;
+ return;
+ }
+
+ // Check if the options are group or not
+ this.optionsGrouped = (value[0] as DropDownGroupElement).dropDownElements !== undefined;
+
+ let defaultOptionFound: boolean = false;
+ let selectedOptionId: number = null;
+ let selectedOptionValue: T = null;
+
+ let optionCount = 0;
+
+ if (this.optionsGrouped) {
+ let options: DropDownGroupElement[] = [];
+ let inputs: DropDownGroupElement[] = (value as DropDownGroupElement[]);
+ inputs.forEach(optGroup => {
+ const optionsGroup: DropDownGroupElement = {
+ displayLabel: optGroup.displayLabel,
+ dropDownElements: []
+ };
+
+ optGroup.dropDownElements.forEach(opt => {
+ optionsGroup.dropDownElements.push({
+ id: optionCount++,
+ displayLabel: opt.displayLabel,
+ value: opt.value,
+ default: opt.default
+ });
+
+ if (optionCount === 1 || opt.default) {
+ selectedOptionId = optionCount - 1;
+ selectedOptionValue = opt.value;
+ defaultOptionFound = defaultOptionFound || opt.default;
+ }
+ });
+
+ options.push(optionsGroup);
+ });
+ this._options = options;
+ }
+ else {
+ let options: DropDownElement[] = [];
+ let inputs: DropDownElement[] = (value as DropDownElement[]);
+ inputs.forEach(opt => {
+ options.push({
+ id: optionCount++,
+ displayLabel: opt.displayLabel,
+ value: opt.value,
+ default: opt.default
+ });
+
+ if (optionCount === 1 || opt.default) {
+ selectedOptionId = optionCount - 1;
+ selectedOptionValue = opt.value;
+ defaultOptionFound = defaultOptionFound || opt.default;
+ }
});
+ this._options = options;
}
- // If there is only 1, auto-select it
- if (this._options.find(d => d.default)) {
- if (this.control) {
- this.onSelectValue(this._options.find(d => d.default).value);
- } else {
- this.onSelect(this._options.find(d => d.default).id.toString());
- }
- } else if (this._options.length > 0) {
- if (this.control) {
- this.onSelectValue(this._options[0].value);
- } else {
- this.onSelect(this._options[0].id.toString());
- }
- } else if (this._options.length === 0) {
+
+ if (optionCount === 0) {
delete this.selectedElement;
+ return;
+ }
+
+ if (!this.control) {
+ this.onSelect(selectedOptionId.toString());
+ }
+ else {
+ this.onSelectValue(selectedOptionValue);
}
+
}
@Input() set resetOnChange(_) {
@@ -86,26 +137,36 @@ export class DropDownComponent implements OnInit, OnChanges {
}
onSelect(id: string) {
- const element = this._options.find(e => e.id.toString() === id);
+ let element: DropDownElement = null;
+ if (this.optionsGrouped) {
+ (this._options as DropDownGroupElement[]).forEach(g => {
+ element = element || g.dropDownElements.find(e => e.id.toString() === id);
+ });
+ }
+ else {
+ element = (this._options as DropDownElement[]).find(e => e.id.toString() === id);
+ }
this.selectedElement = element;
this.value.emit(element.value);
}
onSelectValue(value: T) {
- const element = this._options.find(e => e.value === value);
+ let element: DropDownElement = null;
+ if (this.optionsGrouped) {
+ (this._options as DropDownGroupElement[]).forEach(g => {
+ element = element || g.dropDownElements.find(e => e.value === value);
+ });
+ }
+ else {
+ element = (this._options as DropDownElement[]).find(e => e.value === value);
+ }
this.selectedElement = element;
this.value.emit(element.value);
}
- onBlur(event: any) {
- this.blur.next(event);
- }
-
focus() {
if (this.selectInput) {
- setTimeout(() => {
- this.selectInput.nativeElement.focus();
- });
+ this.selectInput.nativeElement.focus();
}
}
diff --git a/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.html b/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.html
index d6ad531a57..43aa6eca7e 100644
--- a/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.html
+++ b/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.html
@@ -24,4 +24,11 @@
{{ 'readOnlySlots' | translate }} {{ 'appFunctionSettings_functionAppSettings' | translate }} .
-
\ No newline at end of file
+
+
+
diff --git a/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.ts b/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.ts
index fd40b16357..7e761f2d04 100644
--- a/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.ts
+++ b/AzureFunctions.AngularClient/src/app/edit-mode-warning/edit-mode-warning.component.ts
@@ -17,9 +17,10 @@ export class EditModeWarningComponent implements OnInit {
public readOnlySourceControlled = false;
public readWriteSourceControlled = false;
public readOnlySlots = false;
+ public ReadOnlyVSGenerated = false;
ngOnInit() {
- this.functionApp &&
+ if (this.functionApp) {
this.functionApp
.getFunctionAppEditMode()
.subscribe(editMode => {
@@ -31,8 +32,11 @@ export class EditModeWarningComponent implements OnInit {
this.readWriteSourceControlled = true;
} else if (editMode === FunctionAppEditMode.ReadOnlySlots) {
this.readOnlySlots = true;
+ } else if (editMode === FunctionAppEditMode.ReadOnlyVSGenerated) {
+ this.ReadOnlyVSGenerated = true;
}
});
+ }
}
onFunctionAppSettingsClicked() {
diff --git a/AzureFunctions.AngularClient/src/app/extension-install/extension-install.component.scss b/AzureFunctions.AngularClient/src/app/extension-install/extension-install.component.scss
index f852d1b260..de126103c8 100644
--- a/AzureFunctions.AngularClient/src/app/extension-install/extension-install.component.scss
+++ b/AzureFunctions.AngularClient/src/app/extension-install/extension-install.component.scss
@@ -24,10 +24,4 @@
div {
line-height: 22px;
-}
-
-.panel-default {
- .panel-body {
- background-color: $grey-color;
- }
}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.html b/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.html
index 717a79daab..f01a9c867d 100644
--- a/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.html
+++ b/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.html
@@ -5,15 +5,14 @@
(mouseenter)="feature.isHighlighted = true"
(mouseleave)="feature.isHighlighted = false"
(focusout)="checkFeatureGroupBlur()"
- class="feature-group-item">
+ class="list-item feature-group-item">
-
+
-
+
-
+
-
+
diff --git a/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.scss b/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.scss
index 0e8155d10f..5b57cc05ea 100644
--- a/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.scss
+++ b/AzureFunctions.AngularClient/src/app/feature-group/feature-group.component.scss
@@ -1,4 +1,4 @@
-@import '../../sass/main';
+@import '../../sass/common/variables';
.feature-group-header{
margin-bottom: 5px;
@@ -17,24 +17,20 @@
text-overflow: ellipsis;
}
- img{
- height: 16px;
- width: 16px;
+ .icon-small{
vertical-align: middle;
margin-bottom: 2px;
margin-left: 3px;
}
- img.super-icon{
+ .icon-super{
+ display: inline-block;
vertical-align: top;
+ margin-top: -3px;
height: 14px;
width: 14px;
}
- &:hover{
- background-color: $chrome-color;
- }
-
.feature-group-result-title{
background-color: $highlight-text-color;
}
@@ -57,3 +53,12 @@
.feature-group-disabled{
color: darken($chrome-color, 30%);
}
+
+:host-context(#app-root[theme=dark]){
+ .feature-group-item{
+ .feature-group-result-title a{
+ background-color: $highlight-text-color;
+ color: $primary-color;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AzureFunctions.AngularClient/src/app/feature-group/feature-item.ts b/AzureFunctions.AngularClient/src/app/feature-group/feature-item.ts
index f985e37f4f..f970e60b16 100644
--- a/AzureFunctions.AngularClient/src/app/feature-group/feature-item.ts
+++ b/AzureFunctions.AngularClient/src/app/feature-group/feature-item.ts
@@ -1,3 +1,4 @@
+import { ScenarioResult } from './../shared/services/scenario/scenario.models';
import { BroadcastEvent } from 'app/shared/models/broadcast-event';
import { BroadcastService } from './../shared/services/broadcast.service';
import { Subject } from 'rxjs/Subject';
@@ -20,7 +21,7 @@ export class FeatureItem {
public isHighlighted: boolean | null;
public isEmpty: boolean | null; // Used to reserve blank space when filtering results
public highlight: boolean | null;
- public iconUrl = 'images/activity-log.svg';
+ public iconUrl = 'image/activity-log.svg';
public superScriptIconUrl: string | null = null;
public nameFocusable: boolean;
public imageFocusable: boolean;
@@ -53,24 +54,26 @@ export class DisableableFeature extends FeatureItem {
info: string,
imageUrl: string,
_disableInfoStream?: Subject
,
- overrideDisableInfo?: DisableInfo // If the feature is known to be disabled before any async logic, then use this disable immediately
+ overrideDisableInfo?: ScenarioResult // If the feature is known to be disabled before any async logic, then use this disable immediately
) {
super(title, keywords, info, imageUrl);
if (overrideDisableInfo) {
- if (!overrideDisableInfo.enabled) {
- this.warning = overrideDisableInfo.disableMessage;
+
+ // Assumes that all scenario results for feature items are a black list
+ if (overrideDisableInfo.status === 'disabled') {
+ this.warning = overrideDisableInfo.data;
}
- this.enabled = overrideDisableInfo.enabled;
+ this.enabled = overrideDisableInfo.status !== 'disabled';
} else if (_disableInfoStream) {
- this._enabledRxSub = _disableInfoStream.subscribe(info => {
- this.enabled = info.enabled;
+ this._enabledRxSub = _disableInfoStream.subscribe(disableInfo => {
+ this.enabled = disableInfo.enabled;
if (!this.enabled) {
- this.warning = info.disableMessage;
+ this.warning = disableInfo.disableMessage;
}
- })
+ });
}
}
@@ -91,7 +94,7 @@ export class DisableableBladeFeature extends DisableableFeature {
protected _bladeInfo: OpenBladeInfo,
protected _portalService: PortalService,
disableInfoStream?: Subject,
- overrideDisableInfo?: DisableInfo) {
+ overrideDisableInfo?: ScenarioResult) {
super(title, keywords, info, imageUrl, disableInfoStream, overrideDisableInfo);
}
@@ -100,33 +103,6 @@ export class DisableableBladeFeature extends DisableableFeature {
}
}
-export class DisableableDyanmicBladeFeature extends DisableableBladeFeature {
- constructor(
- title: string,
- keywords: string,
- info: string,
- imageUrl: string,
- bladeInfo: OpenBladeInfo,
- portalService: PortalService,
- disableInfoStream?: Subject,
- overrideDisableInfoStream?: DisableInfo) {
-
- super(
- title,
- keywords,
- info,
- imageUrl,
- bladeInfo,
- portalService,
- disableInfoStream,
- overrideDisableInfoStream);
- }
-
- click() {
- this._portalService.openBlade(this._bladeInfo, 'site-manage');
- }
-}
-
export class BladeFeature extends FeatureItem {
constructor(title: string,
keywords: string,
@@ -166,7 +142,7 @@ export class TabFeature extends FeatureItem {
public featureId: string,
private _broadcastService: BroadcastService) {
- super(title, keywords, info, imageUrl, 'images/new-tab.svg');
+ super(title, keywords, info, imageUrl, 'image/new-tab.svg');
}
click() {
diff --git a/AzureFunctions.AngularClient/src/app/file-explorer/file-explorer.component.html b/AzureFunctions.AngularClient/src/app/file-explorer/file-explorer.component.html
index 839e0c561f..79c997840c 100644
--- a/AzureFunctions.AngularClient/src/app/file-explorer/file-explorer.component.html
+++ b/AzureFunctions.AngularClient/src/app/file-explorer/file-explorer.component.html
@@ -1,20 +1,24 @@
-
+
{{ 'fileExplorer_add' | translate }}
-
-
+
+
{{ 'fileExplorer_upload' | translate }}
-
+
+
+
-
{{ 'fileExplorer_delete' | translate }}
-
+
@@ -26,7 +30,7 @@
{{folder.name}}
-
@@ -57,23 +57,23 @@
-
{{fileName}}
+
{{fileName}}
{{ 'save' | translate }}
-
{{ 'functionDev_saveAndRun' | translate }}
-
@@ -92,16 +92,16 @@
-
+
{{ 'functionDev_gerFunctionUrl' | translate }}
-
+
{{ 'functionDev_gerGithubSecret' | translate }}
-
+
{{ 'functionDev_addEventGrid' | translate }}
@@ -123,9 +123,9 @@
- {{ 'logStreaming_logs' | translate }}
+
{{ 'logStreaming_logs' | translate }}
-
+
@@ -147,9 +147,9 @@
-
- {{ 'functionDev_viewFiles' | translate }}
- {{ 'test' | translate }}
+
+ {{ 'functionDev_viewFiles' | translate }}
+ {{ 'test' | translate }}
@@ -158,10 +158,10 @@
- {{ 'functionDev_viewFiles' | translate }}
- {{ 'test' | translate }}
+
{{ 'functionDev_viewFiles' | translate }}
+ {{ 'test' | translate }}
-
+
@@ -181,7 +181,7 @@
-
{{ 'functionDev_requestBody' | translate }}
+
{{ 'functionDev_requestBody' | translate }}
@@ -189,7 +189,7 @@
-
{{ 'functionDev_Output' | translate }}
+
{{ 'functionDev_Output' | translate }}
99 && runResult.statusCode < 300)" class="message-success">
{{ 'functionDev_status' | translate }} {{runResult.statusCode}}
{{runResult.statusText}}
@@ -207,7 +207,7 @@
-
{{ 'functionDev_Output' | translate }}
+
{{ 'functionDev_Output' | translate }}
99 && runResult.statusCode < 300)" class="message-success"> {{ 'functionDev_status' | translate }} {{runResult.statusCode}}
{{runResult.statusText}}
@@ -224,7 +224,7 @@