Browse Source

Custom dropdowns.

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
c150f3c7b2
  1. 4
      src/Squidex/app/features/content/pages/contents/content-item.component.html
  2. 4
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  3. 4
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  4. 14
      src/Squidex/app/framework/angular/autocomplete.component.html
  5. 60
      src/Squidex/app/framework/angular/autocomplete.component.scss
  6. 2
      src/Squidex/app/framework/angular/autocomplete.component.ts
  7. 8
      src/Squidex/app/framework/angular/dropdown.component.html
  8. 40
      src/Squidex/app/framework/angular/dropdown.component.scss
  9. 6
      src/Squidex/app/framework/angular/dropdown.component.ts
  10. 106
      src/Squidex/app/framework/angular/modal-target.directive.ts
  11. 1
      src/Squidex/app/framework/declarations.ts
  12. 3
      src/Squidex/app/framework/module.ts
  13. 4
      src/Squidex/app/shared/components/language-selector.component.html
  14. 33
      src/Squidex/app/theme/_forms.scss
  15. 1
      src/Squidex/app/theme/_vars.scss

4
src/Squidex/app/features/content/pages/contents/content-item.component.html

@ -13,10 +13,10 @@
</td>
<td>
<div class="dropdown dropdown-options" *ngIf="content">
<button type="button" class="btn btn-link btn-decent" (click)="dropdown.toggle(); $event.stopPropagation()" [class.active]="dropdown.isOpen | async">
<button type="button" class="btn btn-link btn-decent" (click)="dropdown.toggle(); $event.stopPropagation()" [class.active]="dropdown.isOpen | async" #optionsButton>
<i class="icon-dots"></i>
</button>
<div class="dropdown-menu" *sqxModalView="dropdown" closeAlways="true" [@fade]>
<div class="dropdown-menu" *sqxModalView="dropdown" closeAlways="true" [sqxModalTarget]="optionsButton" position="right" [@fade]>
<a class="dropdown-item" (click)="publishing.emit(); $event.stopPropagation()" *ngIf="!content.isPublished">
Publish
</a>

4
src/Squidex/app/features/schemas/pages/schema/field.component.html

@ -22,10 +22,10 @@
</button>
<div class="dropdown dropdown-options">
<button type="button" class="btn btn-link btn-decent" (click)="dropdown.toggle()" [class.active]="dropdown.isOpen | async">
<button type="button" class="btn btn-link btn-decent" (click)="dropdown.toggle()" [class.active]="dropdown.isOpen | async" #optionsButton>
<i class="icon-dots"></i>
</button>
<div class="dropdown-menu" *sqxModalView="dropdown" closeAlways="true" [@fade]>
<div class="dropdown-menu" *sqxModalView="dropdown" closeAlways="true" [sqxModalTarget]="optionsButton" position="right" [@fade]>
<a class="dropdown-item" (click)="enabling.emit()" *ngIf="field.isDisabled">
Enable
</a>

4
src/Squidex/app/features/schemas/pages/schema/schema-page.component.html

@ -18,10 +18,10 @@
</div>
<div class="dropdown dropdown-options">
<button type="button" class="btn btn-link btn-decent btn-sm" (click)="editOptionsDropdown.toggle()" [class.active]="editOptionsDropdown.isOpen | async">
<button type="button" class="btn btn-link btn-decent btn-sm" (click)="editOptionsDropdown.toggle()" [class.active]="editOptionsDropdown.isOpen | async" #optionsButton>
<i class="icon-dots"></i>
</button>
<div class="dropdown-menu" *sqxModalView="editOptionsDropdown" closeAlways="true" [@fade]>
<div class="dropdown-menu" *sqxModalView="editOptionsDropdown" closeAlways="true" [sqxModalTarget]="optionsButton" position="right" [@fade]>
<a class="dropdown-item dropdown-item-delete" (click)="confirmDeleteDialog.show()">
Delete
</a>

14
src/Squidex/app/framework/angular/autocomplete.component.html

@ -1,18 +1,16 @@
<span>
<input type="text" class="form-control" (blur)="blur()" [attr.name]="inputName" (keydown)="onKeyDown($event)" [attr.placeholder]="placeholder"
<input type="text" class="form-control" (blur)="blur()" [attr.name]="inputName" (keydown)="onKeyDown($event)" [attr.placeholder]="placeholder" #input
[formControl]="queryInput"
autocomplete="off"
autocorrect="off"
autocapitalize="off">
<div class="items-container" *ngIf="items.length > 0">
<div class="items" #container>
<div *ngFor="let item of items; let i = index;" class="item" [class.active]="i === itemSelection" (mousedown)="chooseItem(item)" (mouseover)="selectIndex(i)" [sqxScrollActive]="i === itemSelection" [container]="container">
<img class="item-image" [attr.src]="item.image" />
<div *ngIf="items.length > 0" [sqxModalTarget]="input" class="control-dropdown" #container>
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === itemSelection" (mousedown)="chooseItem(item)" (mouseover)="selectIndex(i)" [sqxScrollActive]="i === itemSelection" [container]="container">
<img class="control-dropdown-item-image" [attr.src]="item.image" />
<span class="item-title">{{item.title}}</span>
<span class="item-description">{{item.description}}</span>
</div>
<span class="control-dropdown-item-title">{{item.title}}</span>
<span class="control-dropdown-item-description">{{item.description}}</span>
</div>
</div>
</span>

60
src/Squidex/app/framework/angular/autocomplete.component.scss

@ -3,52 +3,26 @@
$color-input-border: rgba(0, 0, 0, .15);
.items {
&-container {
position: relative;
}
.control-dropdown {
& {
@include absolute(2px, auto, auto, 0);
@include border-radius(.25em);
@include box-shadow;
width: 18rem;
max-height: 12rem;
border: 1px solid $color-input-border;
background: $color-dark-foreground;
padding: .3rem 0;
overflow-y: auto;
z-index: 10000;
}
}
.item {
& {
padding: .3rem .8rem;
border: 0;
background: transparent;
cursor: pointer;
}
&.active {
color: $color-dark-foreground;
border: 0;
background: $color-theme-blue;
}
&-image {
@include circle(2.5rem);
float: left;
}
&-title,
&-description {
@include truncate;
margin-left: 3rem;
}
&-description {
font-size: .8rem;
font-style: italic;
&-item {
&-image {
@include circle(2.5rem);
float: left;
}
&-title,
&-description {
@include truncate;
margin-left: 3rem;
}
&-description {
font-size: .8rem;
font-style: italic;
}
}
}

2
src/Squidex/app/framework/angular/autocomplete.component.ts

@ -141,7 +141,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
}
public blur() {
this.reset();
// this.reset();
this.touchedCallback();
}

8
src/Squidex/app/framework/angular/dropdown.component.html

@ -1,11 +1,11 @@
<span>
<div class="selection">
<input type="text" class="form-control" [disabled]="isDisabled" (click)="open()" readonly (keydown)="onKeyDown($event)"
<input type="text" class="form-control" [disabled]="isDisabled" (click)="open()" readonly (keydown)="onKeyDown($event)" #input
autocomplete="off"
autocorrect="off"
autocapitalize="off">
<div class="item" *ngIf="selectedItem">
<div class="control-dropdown-item" *ngIf="selectedItem">
<ng-template [sqxTemplateWrapper]="itemTemplate" [item]="selectedItem"></ng-template>
</div>
@ -13,8 +13,8 @@
</div>
<div class="items-container">
<div class="items" #container *sqxModalView="modalView">
<div *ngFor="let item of items; let i = index;" class="item selectable" [class.active]="i === selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === selectedIndex" [container]="container">
<div class="control-dropdown" #container *sqxModalView="dropdown" [sqxModalTarget]="input">
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === selectedIndex" (mousedown)="selectIndexAndClose(i)" [sqxScrollActive]="i === selectedIndex" [container]="container">
<ng-template [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template>
</div>
</div>

40
src/Squidex/app/framework/angular/dropdown.component.scss

@ -1,7 +1,6 @@
@import '_mixins';
@import '_vars';
$color-input-border: rgba(0, 0, 0, .15);
$color-input-disabled: #eef1f4;
.form-control {
@ -24,7 +23,7 @@ $color-input-disabled: #eef1f4;
overflow: hidden;
}
.item {
.control-dropdown-item {
@include absolute(0, 1rem, 0, 0);
pointer-events: none;
}
@ -34,41 +33,4 @@ $color-input-disabled: #eef1f4;
font-size: .9rem;
font-weight: normal;
}
}
.items {
&-container {
position: relative;
}
& {
@include absolute(2px, auto, auto, 0);
@include border-radius(.25em);
@include box-shadow;
max-height: 12rem;
border: 1px solid $color-input-border;
background: $color-dark-foreground;
padding: .3rem 0;
overflow-y: auto;
z-index: 10000;
}
}
.item {
& {
padding: .5rem .75rem;
}
&.selectable {
& {
cursor: pointer;
}
&.active,
&:hover {
color: $color-dark-foreground;
border: 0;
background: $color-theme-blue;
}
}
}

6
src/Squidex/app/framework/angular/dropdown.component.ts

@ -36,7 +36,7 @@ export class DropdownComponent implements ControlValueAccessor {
@ContentChild(TemplateRef)
public itemTemplate: TemplateRef<any>;
public modalView = new ModalView();
public dropdown = new ModalView();
public selectedItem: any;
public selectedIndex = -1;
@ -79,7 +79,7 @@ export class DropdownComponent implements ControlValueAccessor {
}
public open() {
this.modalView.show();
this.dropdown.show();
this.touchedCallback();
}
@ -89,7 +89,7 @@ export class DropdownComponent implements ControlValueAccessor {
}
private close() {
this.modalView.hide();
this.dropdown.hide();
}
private up() {

106
src/Squidex/app/framework/angular/modal-target.directive.ts

@ -0,0 +1,106 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { AfterViewInit, Directive, ElementRef, Input, OnDestroy, OnInit, Renderer } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
@Directive({
selector: '[sqxModalTarget]'
})
export class ModalTargetDirective implements AfterViewInit, OnDestroy, OnInit {
private elementResizeListener: Function;
private targetResizeListener: Function;
private timer: Subscription;
private targetElement: any;
@Input('sqxModalTarget')
public target: any;
@Input()
public offset = 2;
@Input()
public position = 'left';
constructor(
private readonly renderer: Renderer,
private readonly element: ElementRef
) {
}
public ngOnInit() {
if (this.target) {
this.targetElement = this.target;
this.targetResizeListener =
this.renderer.listen(this.targetElement, 'resize', () => {
this.updatePosition();
});
this.elementResizeListener =
this.renderer.listen(this.element.nativeElement, 'resize', () => {
this.updatePosition();
});
this.timer =
Observable.timer(100, 100).subscribe(() => {
this.updatePosition();
});
}
}
public ngOnDestroy() {
if (this.targetResizeListener) {
this.targetResizeListener();
}
if (this.elementResizeListener) {
this.elementResizeListener();
}
if (this.timer) {
this.timer.unsubscribe();
}
}
public ngAfterViewInit() {
const modalRef = this.element.nativeElement;
this.renderer.setElementStyle(modalRef, 'position', 'fixed');
this.renderer.setElementStyle(modalRef, 'z-index', '1000000');
this.updatePosition();
}
private updatePosition() {
const viewportHeight = document.documentElement.clientHeight;
const modalRef = this.element.nativeElement;
const modalRect = this.element.nativeElement.getBoundingClientRect();
const targetRect: ClientRect = this.targetElement.getBoundingClientRect();
const left = this.position === 'left' ?
targetRect.left :
targetRect.right - modalRect.width;
let top = targetRect.bottom + this.offset;
if (top + modalRect.height > viewportHeight) {
const potentialTop = targetRect.top - modalRect.height - this.offset;
if (potentialTop > 0) {
top = potentialTop;
}
}
this.renderer.setElementStyle(modalRef, 'top', top + 'px');
this.renderer.setElementStyle(modalRef, 'left', left + 'px');
this.renderer.setElementStyle(modalRef, 'right', 'auto');
this.renderer.setElementStyle(modalRef, 'bottom', 'auto');
}
}

1
src/Squidex/app/framework/declarations.ts

@ -24,6 +24,7 @@ export * from './angular/indeterminate-value.directive';
export * from './angular/json-editor.component';
export * from './angular/lowercase-input.directive';
export * from './angular/markdown-editor.component';
export * from './angular/modal-target.directive';
export * from './angular/modal-view.directive';
export * from './angular/money.pipe';
export * from './angular/name.pipe';

3
src/Squidex/app/framework/module.ts

@ -36,6 +36,7 @@ import {
LowerCaseInputDirective,
MarkdownEditorComponent,
MessageBus,
ModalTargetDirective,
ModalViewDirective,
MoneyPipe,
MonthPipe,
@ -92,6 +93,7 @@ import {
JsonEditorComponent,
LowerCaseInputDirective,
MarkdownEditorComponent,
ModalTargetDirective,
ModalViewDirective,
MoneyPipe,
MonthPipe,
@ -134,6 +136,7 @@ import {
JsonEditorComponent,
LowerCaseInputDirective,
MarkdownEditorComponent,
ModalTargetDirective,
ModalViewDirective,
MoneyPipe,
MonthPipe,

4
src/Squidex/app/shared/components/language-selector.component.html

@ -5,10 +5,10 @@
</div>
<div class="dropdown-options btn-group btn-group-{{size}}" *ngIf="isLargeMode">
<button type="button" class="btn btn-secondary dropdown-toggle" [attr.title]="selectedLanguage.englishName" (click)="dropdown.toggle(); $event.stopPropagation()">
<button type="button" class="btn btn-secondary dropdown-toggle" [attr.title]="selectedLanguage.englishName" (click)="dropdown.toggle(); $event.stopPropagation()" #button>
{{selectedLanguage.iso2Code}}
</button>
<div class="dropdown-menu" *sqxModalView="dropdown" closeAlways="true" [@fade]>
<div class="dropdown-menu" *sqxModalView="dropdown" closeAlways="true" [sqxModalTarget]="button" position="right" [@fade]>
<div class="dropdown-item" *ngFor="let language of languages" [class.active]="language == selectedLanguage" (click)="selectLanguage(language)">
<strong class="iso-code">{{language.iso2Code}}</strong> &nbsp; &nbsp; ({{language.englishName}})
</div>

33
src/Squidex/app/theme/_forms.scss

@ -84,6 +84,39 @@
}
}
//
// Control Dropdown item
//
.control-dropdown {
& {
@include absolute(2px, auto, auto, 0);
@include border-radius(.25em);
@include box-shadow;
max-height: 10rem;
border: 1px solid $color-input-border;
background: $color-dark-foreground;
padding: .3rem 0;
overflow-y: auto;
}
&-item {
padding: .5rem .75rem;
}
&-item-selectable {
& {
cursor: pointer;
}
&.active,
&:hover {
color: $color-dark-foreground;
border: 0;
background: $color-theme-blue;
}
}
}
//
// Form group error.
//

1
src/Squidex/app/theme/_vars.scss

@ -10,6 +10,7 @@ $color-subtext: $color-text-decent;
$color-empty: $color-text-decent;
$color-control: rgba(0, 0, 0, .15);
$color-input: #dbe4eb;
$color-input-border: rgba(0, 0, 0, .15);
$color-input-background: #fff;
$color-disabled: #eef1f4;

Loading…
Cancel
Save