Browse Source

Many style improvements.

pull/308/head
Sebastian 8 years ago
parent
commit
dc3ddaad75
  1. 4
      src/Squidex/app/features/assets/pages/assets-page.component.html
  2. 8
      src/Squidex/app/features/assets/pages/assets-page.component.ts
  3. 4
      src/Squidex/app/features/content/shared/assets-editor.component.html
  4. 6
      src/Squidex/app/features/content/shared/assets-editor.component.scss
  5. 4
      src/Squidex/app/framework/angular/forms/tag-editor.component.html
  6. 15
      src/Squidex/app/framework/angular/forms/tag-editor.component.scss
  7. 10
      src/Squidex/app/framework/angular/forms/tag-editor.component.ts
  8. 2
      src/Squidex/app/shared/components/asset.component.html
  9. 17
      src/Squidex/app/shared/components/asset.component.scss
  10. 10
      src/Squidex/app/shared/components/asset.component.ts
  11. 2
      src/Squidex/app/shared/components/assets-list.component.html
  12. 5
      src/Squidex/app/shared/components/assets-list.component.scss
  13. 4
      src/Squidex/app/shared/services/assets.service.spec.ts
  14. 6
      src/Squidex/app/shared/services/assets.service.ts
  15. 35
      src/Squidex/app/shared/state/assets.state.spec.ts
  16. 50
      src/Squidex/app/shared/state/assets.state.ts
  17. 3
      src/Squidex/app/theme/_vars.scss

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

@ -24,7 +24,7 @@
<ng-container sidebar> <ng-container sidebar>
<div class="section"> <div class="section">
<a class="row tag" (click)="selectTag(undefined)" [class.active]="!assetsState.snapshot.tag"> <a class="row tag" (click)="resetTags()" [class.active]="assetsState.isTagSelectionEmpty()">
<div class="col"> <div class="col">
All Assets All Assets
</div> </div>
@ -35,7 +35,7 @@
<h3>Tags</h3> <h3>Tags</h3>
<ng-container *ngIf="assetsState.tags | async; let tags"> <ng-container *ngIf="assetsState.tags | async; let tags">
<a class="row tag" *ngFor="let tag of tags | sqxKeys" (click)="selectTag(tag)" [class.active]="tag === assetsState.snapshot.tag"> <a class="row tag" *ngFor="let tag of tags | sqxKeys" (click)="toggleTag(tag)" [class.active]="assetsState.isTagSelected(tag)">
<div class="col"> <div class="col">
{{tag}} {{tag}}
</div> </div>

8
src/Squidex/app/features/assets/pages/assets-page.component.ts

@ -39,8 +39,12 @@ export class AssetsPageComponent implements OnInit {
this.assetsState.search(this.assetsFilter.value).pipe(onErrorResumeNext()).subscribe(); this.assetsState.search(this.assetsFilter.value).pipe(onErrorResumeNext()).subscribe();
} }
public selectTag(tag: string) { public resetTags() {
this.assetsState.selectTag(tag).pipe(onErrorResumeNext()).subscribe(); this.assetsState.resetTags().pipe(onErrorResumeNext()).subscribe();
}
public toggleTag(tag: string) {
this.assetsState.toggleTag(tag).pipe(onErrorResumeNext()).subscribe();
} }
public goNext() { public goNext() {

4
src/Squidex/app/features/content/shared/assets-editor.component.html

@ -1,10 +1,8 @@
<div class="assets-container" [class.disabled]="isDisabled"> <div class="assets-container" [class.disabled]="isDisabled">
<div class="row"> <div class="row">
<div class="drop-area-container"> <div class="drop-area align-items-center" (sqxFileDrop)="addFiles($event)" (click)="assetsDialog.show()">
<div class="drop-area" (sqxFileDrop)="addFiles($event)" (click)="assetsDialog.show()">
Drop files here to add them. Drop files here to add them.
</div> </div>
</div>
<sqx-asset *ngFor="let file of newAssets" [initFile]="file" <sqx-asset *ngFor="let file of newAssets" [initFile]="file"
(failed)="onAssetFailed(file)" (failed)="onAssetFailed(file)"

6
src/Squidex/app/features/content/shared/assets-editor.component.scss

@ -22,13 +22,15 @@
& { & {
@include transition(border-color .4s ease); @include transition(border-color .4s ease);
@include border-radius; @include border-radius;
@include flex-box;
border: 2px dashed $color-border; border: 2px dashed $color-border;
height: $asset-height; height: $asset-height + 1.5rem;
width: $asset-height; width: $asset-height;
margin-left: 8px;
font-size: 1.2rem; font-size: 1.2rem;
font-weight: normal; font-weight: normal;
text-align: center; text-align: center;
padding: 3.5rem 2rem; padding: 0 2rem;
cursor: pointer; cursor: pointer;
color: darken($color-border, 30%); color: darken($color-border, 30%);
} }

4
src/Squidex/app/framework/angular/forms/tag-editor.component.html

@ -1,9 +1,9 @@
<div class="form-control {{class}}" (click)="input.focus()" [class.focus]="hasFocus"> <div class="form-control {{class}}" (click)="input.focus()" [class.focus]="hasFocus" [class.disabled]="addInput.disabled">
<span class="item" *ngFor="let item of items; let i = index" [class.disabled]="addInput.disabled"> <span class="item" *ngFor="let item of items; let i = index" [class.disabled]="addInput.disabled">
{{item}} <i class="icon-close" (click)="remove(i)"></i> {{item}} <i class="icon-close" (click)="remove(i)"></i>
</span> </span>
<input type="text" class="blank" [attr.name]="inputName" (keydown)="onKeyDown($event)" #input <input type="text" class="blank" [attr.name]="inputName" (keydown)="onKeyDown($event)" [class.hidden]="addInput.disabled" #input
(focus)="focus()" (focus)="focus()"
(blur)="markTouched()" (blur)="markTouched()"
(input)="adjustSize($event.target)" (input)="adjustSize($event.target)"

15
src/Squidex/app/framework/angular/forms/tag-editor.component.scss

@ -6,6 +6,10 @@
cursor: text; cursor: text;
} }
&.disabled {
cursor: inherit;
}
&.focus { &.focus {
@include box-shadow-raw(0 0 0 0.2rem rgba(51, 137, 255, 0.25)); @include box-shadow-raw(0 0 0 0.2rem rgba(51, 137, 255, 0.25));
border-color: #b3d3ff; border-color: #b3d3ff;
@ -26,6 +30,10 @@
@include box-shadow-none; @include box-shadow-none;
outline: none; outline: none;
} }
&:hover {
background: transparent;
}
} }
.icon-close { .icon-close {
@ -62,16 +70,9 @@
} }
&.disabled { &.disabled {
& {
pointer-events: none; pointer-events: none;
} }
&,
&:hover {
background: $color-theme-blue-light;
}
}
&:hover { &:hover {
background: $color-theme-blue-dark; background: $color-theme-blue-dark;
} }

10
src/Squidex/app/framework/angular/forms/tag-editor.component.ts

@ -123,13 +123,11 @@ export class TagEditorComponent implements ControlValueAccessor {
this.callTouched = fn; this.callTouched = fn;
} }
public remove(index: number) {
this.updateItems([...this.items.slice(0, index), ...this.items.splice(index + 1)]);
}
public focus() { public focus() {
if (this.addInput.enabled) {
this.hasFocus = true; this.hasFocus = true;
} }
}
private resetForm() { private resetForm() {
this.addInput.reset(); this.addInput.reset();
@ -143,6 +141,10 @@ export class TagEditorComponent implements ControlValueAccessor {
this.hasFocus = false; this.hasFocus = false;
} }
public remove(index: number) {
this.updateItems([...this.items.slice(0, index), ...this.items.splice(index + 1)]);
}
public adjustSize() { public adjustSize() {
const style = window.getComputedStyle(this.inputElement.nativeElement); const style = window.getComputedStyle(this.inputElement.nativeElement);

2
src/Squidex/app/shared/components/asset.component.html

@ -53,7 +53,7 @@
</form> </form>
</div> </div>
</div> </div>
<div> <div class="tags">
<sqx-tag-editor [useDefaultValue]="false" [formControl]="tagInput" class="blank"></sqx-tag-editor> <sqx-tag-editor [useDefaultValue]="false" [formControl]="tagInput" class="blank"></sqx-tag-editor>
</div> </div>
<div class="file-info"> <div class="file-info">

17
src/Squidex/app/shared/components/asset.component.scss

@ -49,13 +49,6 @@
} }
} }
:host {
width: $asset-height;
padding-bottom: 1rem;
padding-left: 8px;
padding-right: 8px;
}
.drop-overlay { .drop-overlay {
& { & {
@include overlay; @include overlay;
@ -73,7 +66,11 @@
.card { .card {
& { & {
@include overlay-container; @include overlay-container;
min-height: $asset-height + 1.5rem; width: $asset-width;
margin-bottom: 1rem;
margin-left: 8px;
margin-right: 8px;
min-height: $asset-height;
} }
&.selectable { &.selectable {
@ -189,3 +186,7 @@
.editable { .editable {
height: 2rem; height: 2rem;
} }
.tags {
min-height: 26px;
}

10
src/Squidex/app/shared/components/asset.component.ts

@ -110,6 +110,10 @@ export class AssetComponent implements OnDestroy, OnInit {
this.updateAsset(this.asset, false); this.updateAsset(this.asset, false);
} }
if (this.isDisabled) {
this.tagInput.disable();
}
this.tagSubscription = this.tagSubscription =
this.tagInput.valueChanges.pipe( this.tagInput.valueChanges.pipe(
distinctUntilChanged(), distinctUntilChanged(),
@ -184,12 +188,6 @@ export class AssetComponent implements OnDestroy, OnInit {
this.renaming = false; this.renaming = false;
} }
public startTagging() {
if (!this.isDisabled) {
this.isTagging = true;
}
}
private setProgress(progress = 0) { private setProgress(progress = 0) {
this.progress = progress; this.progress = progress;
} }

2
src/Squidex/app/shared/components/assets-list.component.html

@ -14,7 +14,7 @@
<div class="file-drop-info">Drop file on existing item to replace the asset with a newer version.</div> <div class="file-drop-info">Drop file on existing item to replace the asset with a newer version.</div>
</div> </div>
<div class="row"> <div class="row assets">
<sqx-asset *ngFor="let file of newFiles" [initFile]="file" <sqx-asset *ngFor="let file of newFiles" [initFile]="file"
(failed)="remove(file)" (failed)="remove(file)"
(loaded)="add(file, $event)"> (loaded)="add(file, $event)">

5
src/Squidex/app/shared/components/assets-list.component.scss

@ -35,6 +35,11 @@
} }
} }
.assets {
margin-left: -8px;
margin-right: -8px;
}
.btn { .btn {
cursor: default; cursor: default;
} }

4
src/Squidex/app/shared/services/assets.service.spec.ts

@ -272,9 +272,9 @@ describe('AssetsService', () => {
it('should append query to find by name and tag', it('should append query to find by name and tag',
inject([AssetsService, HttpTestingController], (assetsService: AssetsService, httpMock: HttpTestingController) => { inject([AssetsService, HttpTestingController], (assetsService: AssetsService, httpMock: HttpTestingController) => {
assetsService.getAssets('my-app', 17, 13, 'my-query', 'tag1').subscribe(); assetsService.getAssets('my-app', 17, 13, 'my-query', ['tag1', 'tag2']).subscribe();
const req = httpMock.expectOne(`http://service/p/api/apps/my-app/assets?$filter=contains(fileName,'my-query') and tags eq 'tag1'&$top=17&$skip=13`); const req = httpMock.expectOne(`http://service/p/api/apps/my-app/assets?$filter=contains(fileName,'my-query') and tags eq 'tag1' and tags eq 'tag2'&$top=17&$skip=13`);
expect(req.request.method).toEqual('GET'); expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull(); expect(req.request.headers.get('If-Match')).toBeNull();

6
src/Squidex/app/shared/services/assets.service.ts

@ -131,7 +131,7 @@ export class AssetsService {
map(response => <any>response)); map(response => <any>response));
} }
public getAssets(appName: string, take: number, skip: number, query?: string, tag?: string, ids?: string[]): Observable<AssetsDto> { public getAssets(appName: string, take: number, skip: number, query?: string, tags?: string[], ids?: string[]): Observable<AssetsDto> {
let fullQuery = ''; let fullQuery = '';
if (ids) { if (ids) {
@ -145,9 +145,13 @@ export class AssetsService {
filters.push(`contains(fileName,'${encodeURIComponent(query)}')`); filters.push(`contains(fileName,'${encodeURIComponent(query)}')`);
} }
if (tags) {
for (let tag of tags) {
if (tag && tag.length > 0) { if (tag && tag.length > 0) {
filters.push(`tags eq '${encodeURIComponent(tag)}'`); filters.push(`tags eq '${encodeURIComponent(tag)}'`);
} }
}
}
if (filters.length > 0) { if (filters.length > 0) {
queries.push(`$filter=${filters.join(' and ')}`); queries.push(`$filter=${filters.join(' and ')}`);

35
src/Squidex/app/shared/state/assets.state.spec.ts

@ -49,7 +49,7 @@ describe('AssetsState', () => {
assetsService = Mock.ofType<AssetsService>(); assetsService = Mock.ofType<AssetsService>();
assetsService.setup(x => x.getAssets(app, 30, 0, undefined, undefined)) assetsService.setup(x => x.getAssets(app, 30, 0, undefined, []))
.returns(() => of(new AssetsDto(200, oldAssets))); .returns(() => of(new AssetsDto(200, oldAssets)));
assetsService.setup(x => x.getTags(app)) assetsService.setup(x => x.getTags(app))
@ -66,7 +66,7 @@ describe('AssetsState', () => {
expect(assetsState.snapshot.assetsPager.numberOfItems).toEqual(200); expect(assetsState.snapshot.assetsPager.numberOfItems).toEqual(200);
expect(assetsState.snapshot.isLoaded).toBeTruthy(); expect(assetsState.snapshot.isLoaded).toBeTruthy();
assetsService.verify(x => x.getAssets(app, 30, 0, undefined, undefined), Times.exactly(2)); assetsService.verify(x => x.getAssets(app, 30, 0, undefined, []), Times.exactly(2));
assetsService.verify(x => x.getTags(app), Times.exactly(2)); assetsService.verify(x => x.getTags(app), Times.exactly(2));
dialogs.verify(x => x.notifyInfo(It.isAnyString()), Times.never()); dialogs.verify(x => x.notifyInfo(It.isAnyString()), Times.never());
@ -112,7 +112,7 @@ describe('AssetsState', () => {
}); });
it('should load next page and prev page when paging', () => { it('should load next page and prev page when paging', () => {
assetsService.setup(x => x.getAssets(app, 30, 30, undefined, undefined)) assetsService.setup(x => x.getAssets(app, 30, 30, undefined, []))
.returns(() => of(new AssetsDto(200, []))); .returns(() => of(new AssetsDto(200, [])));
assetsState.goNext().subscribe(); assetsState.goNext().subscribe();
@ -120,29 +120,40 @@ describe('AssetsState', () => {
expect().nothing(); expect().nothing();
assetsService.verify(x => x.getAssets(app, 30, 30, undefined, undefined), Times.once()); assetsService.verify(x => x.getAssets(app, 30, 30, undefined, []), Times.once());
assetsService.verify(x => x.getAssets(app, 30, 0, undefined, undefined), Times.exactly(2)); assetsService.verify(x => x.getAssets(app, 30, 0, undefined, []), Times.exactly(2));
}); });
it('should load with query when searching', () => { it('should load with query when searching', () => {
assetsService.setup(x => x.getAssets(app, 30, 0, 'my-query', undefined)) assetsService.setup(x => x.getAssets(app, 30, 0, 'my-query', []))
.returns(() => of(new AssetsDto(0, []))); .returns(() => of(new AssetsDto(0, [])));
assetsState.search('my-query').subscribe(); assetsState.search('my-query').subscribe();
expect(assetsState.snapshot.assetsQuery).toEqual('my-query'); expect(assetsState.snapshot.assetsQuery).toEqual('my-query');
assetsService.verify(x => x.getAssets(app, 30, 0, 'my-query', undefined), Times.once()); assetsService.verify(x => x.getAssets(app, 30, 0, 'my-query', []), Times.once());
}); });
it('should load with tag when tag changed', () => { it('should load with tags when tag toggled', () => {
assetsService.setup(x => x.getAssets(app, 30, 0, undefined, 'tag1')) assetsService.setup(x => x.getAssets(app, 30, 0, undefined, ['tag1']))
.returns(() => of(new AssetsDto(0, []))); .returns(() => of(new AssetsDto(0, [])));
assetsState.selectTag('tag1').subscribe(); assetsState.toggleTag('tag1').subscribe();
expect(assetsState.snapshot.tag).toEqual('tag1'); expect(assetsState.isTagSelected('tag1')).toBeTruthy();
assetsService.verify(x => x.getAssets(app, 30, 0, undefined, 'tag1'), Times.once()); assetsService.verify(x => x.getAssets(app, 30, 0, undefined, ['tag1']), Times.once());
});
it('should load without tags when tags reset', () => {
assetsService.setup(x => x.getAssets(app, 30, 0, undefined, []))
.returns(() => of(new AssetsDto(0, [])));
assetsState.resetTags().subscribe();
expect(assetsState.isTagSelectionEmpty()).toBeTruthy();
assetsService.verify(x => x.getAssets(app, 30, 0, undefined, []), Times.exactly(2));
}); });
}); });

50
src/Squidex/app/shared/state/assets.state.ts

@ -22,7 +22,7 @@ import { AppsState } from './apps.state';
interface Snapshot { interface Snapshot {
tags: { [name: string]: number }; tags: { [name: string]: number };
tag?: string; tagsSelected: { [name: string]: boolean };
assets: ImmutableArray<AssetDto>; assets: ImmutableArray<AssetDto>;
assetsPager: Pager; assetsPager: Pager;
@ -37,10 +37,6 @@ export class AssetsState extends State<Snapshot> {
this.changes.pipe(map(x => x.tags), this.changes.pipe(map(x => x.tags),
distinctUntilChanged()); distinctUntilChanged());
public tag =
this.changes.pipe(map(x => x.tag),
distinctUntilChanged());
public assets = public assets =
this.changes.pipe(map(x => x.assets), this.changes.pipe(map(x => x.assets),
distinctUntilChanged()); distinctUntilChanged());
@ -58,7 +54,7 @@ export class AssetsState extends State<Snapshot> {
private readonly assetsService: AssetsService, private readonly assetsService: AssetsService,
private readonly dialogs: DialogService private readonly dialogs: DialogService
) { ) {
super({ assets: ImmutableArray.empty(), assetsPager: new Pager(0, 0, 30), tags: {} }); super({ assets: ImmutableArray.empty(), assetsPager: new Pager(0, 0, 30), tags: {}, tagsSelected: {} });
} }
public load(isReload = false): Observable<any> { public load(isReload = false): Observable<any> {
@ -71,7 +67,11 @@ export class AssetsState extends State<Snapshot> {
private loadInternal(isReload = false): Observable<any> { private loadInternal(isReload = false): Observable<any> {
return combineLatest( return combineLatest(
this.assetsService.getAssets(this.appName, this.snapshot.assetsPager.pageSize, this.snapshot.assetsPager.skip, this.snapshot.assetsQuery, this.snapshot.tag), this.assetsService.getAssets(this.appName,
this.snapshot.assetsPager.pageSize,
this.snapshot.assetsPager.skip,
this.snapshot.assetsQuery,
Object.keys(this.snapshot.tagsSelected)),
this.assetsService.getTags(this.appName) this.assetsService.getTags(this.appName)
).pipe( ).pipe(
tap(dtos => { tap(dtos => {
@ -106,16 +106,18 @@ export class AssetsState extends State<Snapshot> {
const assetsPager = s.assetsPager.decrementCount(); const assetsPager = s.assetsPager.decrementCount();
const tags = { ...s.tags }; const tags = { ...s.tags };
const tagsSelected = { ...s.tagsSelected };
for (let tag of asset.tags) { for (let tag of asset.tags) {
if (tags[tag] === 1) { if (tags[tag] === 1) {
delete tags[tag]; delete tags[tag];
delete tagsSelected[tag];
} else { } else {
tags[tag]--; tags[tag]--;
} }
} }
return { ...s, assets, assetsPager, tags }; return { ...s, assets, assetsPager, tags, tagsSelected };
}); });
}), }),
notify(this.dialogs)); notify(this.dialogs));
@ -126,11 +128,13 @@ export class AssetsState extends State<Snapshot> {
const previous = s.assets.find(x => x.id === asset.id); const previous = s.assets.find(x => x.id === asset.id);
const tags = { ...s.tags }; const tags = { ...s.tags };
const tagsSelected = { ...s.tagsSelected };
if (previous) { if (previous) {
for (let tag of previous.tags) { for (let tag of previous.tags) {
if (tags[tag] === 1) { if (tags[tag] === 1) {
delete tags[tag]; delete tags[tag];
delete tagsSelected[tag];
} else { } else {
tags[tag]--; tags[tag]--;
} }
@ -149,12 +153,28 @@ export class AssetsState extends State<Snapshot> {
const assets = s.assets.replaceBy('id', asset); const assets = s.assets.replaceBy('id', asset);
return { ...s, assets, tags }; return { ...s, assets, tags, tagsSelected };
});
}
public toggleTag(tag: string): Observable<any> {
this.next(s => {
const tagsSelected = { ...s.tagsSelected };
if (tagsSelected[tag]) {
delete tagsSelected[tag];
} else {
tagsSelected[tag] = true;
}
return { ...s, assetsPager: new Pager(0, 0, 30), tagsSelected };
}); });
return this.loadInternal();
} }
public selectTag(tag: string): Observable<any> { public resetTags(): Observable<any> {
this.next(s => ({ ...s, assetsPager: new Pager(0, 0, 30), tag })); this.next(s => ({ ...s, assetsPager: new Pager(0, 0, 30), tagsSelected: {} }));
return this.loadInternal(); return this.loadInternal();
} }
@ -177,6 +197,14 @@ export class AssetsState extends State<Snapshot> {
return this.loadInternal(); return this.loadInternal();
} }
public isTagSelected(tag: string) {
return this.snapshot.tagsSelected[tag] === true;
}
public isTagSelectionEmpty() {
return Object.keys(this.snapshot.tagsSelected).length === 0;
}
private get appName() { private get appName() {
return this.appsState.appName; return this.appsState.appName;
} }

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

@ -97,4 +97,5 @@ $panel-header: 5.4rem;
$panel-sidebar: 3.75rem; $panel-sidebar: 3.75rem;
$panel-light-background: #fff; $panel-light-background: #fff;
$asset-height: 16rem; $asset-width: 16rem;
$asset-height: 18rem;
Loading…
Cancel
Save