mirror of https://github.com/Squidex/squidex.git
15 changed files with 337 additions and 43 deletions
@ -0,0 +1,22 @@ |
|||
<span> |
|||
<div class="selection"> |
|||
<input type="text" class="form-control" [disabled]="isDisabled" (click)="open()" readonly (keydown)="onKeyDown($event)" |
|||
autocomplete="off" |
|||
autocorrect="off" |
|||
autocapitalize="off"> |
|||
|
|||
<div class="item" *ngIf="selectedItem"> |
|||
<ng-template [sqxTemplateWrapper]="itemTemplate" [item]="selectedItem"></ng-template> |
|||
</div> |
|||
|
|||
<i class="icon-caret-down"></i> |
|||
</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"> |
|||
<ng-template [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</span> |
|||
@ -0,0 +1,74 @@ |
|||
@import '_mixins'; |
|||
@import '_vars'; |
|||
|
|||
$color-input-border: rgba(0, 0, 0, .15); |
|||
$color-input-disabled: #eef1f4; |
|||
|
|||
.form-control { |
|||
& { |
|||
width: 100%; |
|||
} |
|||
|
|||
&[readonly] { |
|||
background: $color-input-background; |
|||
} |
|||
|
|||
&:disabled { |
|||
background: $color-input-disabled; |
|||
} |
|||
} |
|||
|
|||
.selection { |
|||
& { |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.item { |
|||
@include absolute(0, 1rem, 0, 0); |
|||
pointer-events: none; |
|||
} |
|||
|
|||
.icon-caret-down { |
|||
@include absolute(30%, .4rem, auto, auto); |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { Component, ContentChild, forwardRef, Input, TemplateRef } from '@angular/core'; |
|||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|||
|
|||
const KEY_ENTER = 13; |
|||
const KEY_ESCAPE = 27; |
|||
const KEY_UP = 38; |
|||
const KEY_DOWN = 40; |
|||
const NOOP = () => { /* NOOP */ }; |
|||
|
|||
import { ModalView } from './../utils/modal-view'; |
|||
|
|||
export const SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { |
|||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DropdownComponent), multi: true |
|||
}; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-dropdown', |
|||
styleUrls: ['./dropdown.component.scss'], |
|||
templateUrl: './dropdown.component.html', |
|||
providers: [SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR] |
|||
}) |
|||
export class DropdownComponent implements ControlValueAccessor { |
|||
private changeCallback: (value: any) => void = NOOP; |
|||
private touchedCallback: () => void = NOOP; |
|||
|
|||
@Input() |
|||
public items: any[] = []; |
|||
|
|||
@ContentChild(TemplateRef) |
|||
public itemTemplate: TemplateRef<any>; |
|||
|
|||
public modalView = new ModalView(); |
|||
|
|||
public selectedItem: any; |
|||
public selectedIndex = -1; |
|||
|
|||
public isDisabled = false; |
|||
|
|||
private get safeItems(): any[] { |
|||
return this.items || []; |
|||
} |
|||
|
|||
public writeValue(value: any) { |
|||
this.selectIndex(this.items && value ? this.items.indexOf(value) : 0); |
|||
} |
|||
|
|||
public setDisabledState(isDisabled: boolean): void { |
|||
this.isDisabled = isDisabled; |
|||
} |
|||
|
|||
public registerOnChange(fn: any) { |
|||
this.changeCallback = fn; |
|||
} |
|||
|
|||
public registerOnTouched(fn: any) { |
|||
this.touchedCallback = fn; |
|||
} |
|||
|
|||
public onKeyDown(event: KeyboardEvent) { |
|||
switch (event.keyCode) { |
|||
case KEY_UP: |
|||
this.up(); |
|||
return false; |
|||
case KEY_DOWN: |
|||
this.down(); |
|||
return false; |
|||
case KEY_ESCAPE: |
|||
case KEY_ENTER: |
|||
this.close(); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public open() { |
|||
this.modalView.show(); |
|||
this.touchedCallback(); |
|||
} |
|||
|
|||
public selectIndexAndClose(selectedIndex: number) { |
|||
this.selectIndex(selectedIndex); |
|||
this.close(); |
|||
} |
|||
|
|||
private close() { |
|||
this.modalView.hide(); |
|||
} |
|||
|
|||
private up() { |
|||
this.selectIndex(this.selectedIndex - 1); |
|||
} |
|||
|
|||
private down() { |
|||
this.selectIndex(this.selectedIndex + 1); |
|||
} |
|||
|
|||
private selectIndex(selectedIndex: number) { |
|||
if (selectedIndex < 0) { |
|||
selectedIndex = 0; |
|||
} |
|||
|
|||
const items = this.items || []; |
|||
|
|||
if (selectedIndex >= items.length) { |
|||
selectedIndex = items.length - 1; |
|||
} |
|||
|
|||
const value = items[selectedIndex]; |
|||
|
|||
if (value !== this.selectedItem) { |
|||
this.selectedIndex = selectedIndex; |
|||
this.selectedItem = value; |
|||
|
|||
this.changeCallback(value); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { Directive, Input, OnDestroy, OnInit, OnChanges, TemplateRef, ViewContainerRef, EmbeddedViewRef } from '@angular/core'; |
|||
|
|||
@Directive({ |
|||
selector: '[sqxTemplateWrapper]' |
|||
}) |
|||
export class TemplateWrapper implements OnInit, OnDestroy, OnChanges { |
|||
@Input() |
|||
public item: any; |
|||
|
|||
@Input() |
|||
public index: number; |
|||
|
|||
@Input('sqxTemplateWrapper') |
|||
public templateRef: TemplateRef<any>; |
|||
|
|||
public view: EmbeddedViewRef<any>; |
|||
|
|||
public constructor( |
|||
private viewContainer: ViewContainerRef |
|||
) { |
|||
} |
|||
|
|||
public ngOnChanges() { |
|||
if (this.view) { |
|||
this.view.context.$implicit = this.item; |
|||
this.view.context.index = this.index; |
|||
} |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.view = this.viewContainer.createEmbeddedView(this.templateRef, { |
|||
'\$implicit': this.item, |
|||
'index': this.index |
|||
}); |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.view.destroy(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue