mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ADF-1841] Content Metadata first iteration (#2666)
* First try * Small layout changes * Add pipe support for CardViewTextItemModel * property service * Additional stuff * Make CardViewUpdateService smarter * Content metadata saving * Rebase fix * CardView Style fixes * Fix core and content-services tests * Fix CardView text item update UX
This commit is contained in:
parent
15cbd3a316
commit
4b76e6b4a9
@ -1,6 +1,22 @@
|
||||
<ng-container *ngIf="nodeId">
|
||||
<adf-viewer [fileNodeId]="nodeId" [allowSidebar]="true">
|
||||
|
||||
<ng-template let-node="node" #sidebarTemplate>
|
||||
<adf-info-drawer title="Details">
|
||||
<adf-info-drawer-tab label="Properties">
|
||||
<adf-content-metadata-card [node]="node"></adf-content-metadata-card>
|
||||
</adf-info-drawer-tab>
|
||||
|
||||
<adf-info-drawer-tab label="Versions">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
Versions go here...
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</adf-info-drawer-tab>
|
||||
</adf-info-drawer>
|
||||
</ng-template>
|
||||
|
||||
<adf-viewer [fileNodeId]="nodeId" [allowSidebar]="true" [sidebarTemplate]="sidebarTemplate">
|
||||
<!--
|
||||
<adf-viewer-extension [supportedExtensions]="['json']">
|
||||
<ng-template let-urlFileContent="urlFileContent" let-extension="extension">
|
||||
|
@ -129,6 +129,26 @@ const textItemProperty = new CardViewTextItemModel(options);
|
||||
| editable | boolean | false | Whether the property editable or not |
|
||||
| clickable | boolean | false | Whether the property clickable or not |
|
||||
| multiline | string | false | Single or multiline text |
|
||||
| pipes | CardViewTextItemPipeProperty[] | [] | Pipes to be applied on the displayValue |
|
||||
|
||||
##### Using pipes in Card Text Item
|
||||
|
||||
You can use pipes for text items almost the same way as you would do it in your template. You can provide an array of pipes with additional pipeParameters like this:
|
||||
|
||||
```js
|
||||
const myWonderfulPipe1: PipeTransform = <whatever PipeTransform implmentation>;
|
||||
const myWonderfulPipe2: PipeTransform = <whatever PipeTransform implmentation>;
|
||||
|
||||
new CardViewTextItemModel({
|
||||
label: 'some label',
|
||||
value: someValue,
|
||||
key: 'some-key',
|
||||
pipes: [
|
||||
{ pipe: myWonderfulPipe1, params: ['first-param', 'second-param'] },
|
||||
{ pipe: myWonderfulPipe2, params: ['first-param', 'second-param'] }
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
#### Card Map Item
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
<mat-card *ngIf="node">
|
||||
<mat-card-content>
|
||||
<adf-content-metadata [node]="node" [editable]="editable" [maxPropertiesToShow]="maxPropertiesToShow"></adf-content-metadata>
|
||||
</mat-card-content>
|
||||
<mat-card-footer class="adf-viewer-default-sidebar-card-footer" fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||
<div>
|
||||
<button mat-icon-button>
|
||||
<mat-icon>star_border</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="toggleEdit()">
|
||||
<mat-icon>mode_edit</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button mat-button (click)="toggleExpanded()">
|
||||
<ng-container *ngIf="!expanded">
|
||||
<span>{{ 'ADF_VIEWER.SIDEBAR.METADATA.MORE_INFORMATION' | translate }}</span>
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="expanded">
|
||||
<span>{{ 'ADF_VIEWER.SIDEBAR.METADATA.LESS_INFORMATION' | translate }}</span>
|
||||
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||
</ng-container>
|
||||
</button>
|
||||
</mat-card-footer>
|
||||
</mat-card>
|
@ -0,0 +1,21 @@
|
||||
@mixin adf-content-metadata-card-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf-viewer-default-sidebar {
|
||||
|
||||
&-card-footer.mat-card-footer {
|
||||
padding: 8px 12px;
|
||||
border-top: 1px solid mat-color($foreground, text, 0.07);
|
||||
|
||||
button {
|
||||
color: mat-color($foreground, text, 0.54);
|
||||
|
||||
&:hover {
|
||||
color: mat-color($foreground, text, 0.87);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, ViewEncapsulation } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
|
||||
const PROPERTY_COUNTER_WHILE_COLLAPSED = 5;
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-metadata-card',
|
||||
templateUrl: './content-metadata-card.component.html',
|
||||
styleUrls: ['./content-metadata-card.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { 'class': 'adf-viewer-default-sidebar' }
|
||||
})
|
||||
export class ContentMetadataCardComponent {
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
editable: boolean = false;
|
||||
expanded: boolean = false;
|
||||
|
||||
toggleEdit(): void {
|
||||
this.editable = !this.editable;
|
||||
}
|
||||
|
||||
toggleExpanded(): void {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
|
||||
get maxPropertiesToShow(): number {
|
||||
return this.expanded ? Infinity : PROPERTY_COUNTER_WHILE_COLLAPSED;
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
<div class="adf-metadata-properties">
|
||||
<adf-card-view [properties]="properties" [editable]="editable"></adf-card-view>
|
||||
</div>
|
@ -0,0 +1,29 @@
|
||||
@mixin adf-content-metadata-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf {
|
||||
&-metadata-properties {
|
||||
|
||||
.adf-property {
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.adf-property-label {
|
||||
display: block;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
color: mat-color($foreground, text, 0.4);
|
||||
}
|
||||
|
||||
.adf-property-value {
|
||||
display: block;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
color: mat-color($foreground, text, 0.87);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { CardViewItem, CardViewUpdateService, FileSizePipe, NodesApiService } from '@alfresco/adf-core';
|
||||
import { ContentMetadataService } from './content-metadata.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-metadata',
|
||||
templateUrl: './content-metadata.component.html',
|
||||
styleUrls: ['./content-metadata.component.scss'],
|
||||
host: { 'class': 'adf-content-metadata' },
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
providers: [ CardViewUpdateService ],
|
||||
viewProviders: [ ContentMetadataService, FileSizePipe ]
|
||||
})
|
||||
export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
@Input()
|
||||
editable: boolean = false;
|
||||
|
||||
@Input()
|
||||
maxPropertiesToShow: number = Infinity;
|
||||
|
||||
properties: CardViewItem[] = [];
|
||||
|
||||
constructor(private contentMetadataService: ContentMetadataService,
|
||||
private cardViewUpdateService: CardViewUpdateService,
|
||||
private nodesApi: NodesApiService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.cardViewUpdateService.itemUpdated$
|
||||
.switchMap(this.saveNode.bind(this))
|
||||
.subscribe(
|
||||
node => this.node = node,
|
||||
error => this.handleError(error)
|
||||
);
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.recalculateProperties();
|
||||
}
|
||||
|
||||
private saveNode({ changed: nodeBody }): Observable<MinimalNodeEntryEntity> {
|
||||
return this.nodesApi.updateNode(this.node.id, nodeBody);
|
||||
}
|
||||
|
||||
private handleError(error): void {
|
||||
/*tslint:disable-next-line*/
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
private recalculateProperties(): void {
|
||||
let basicProperties = this.contentMetadataService.getBasicProperties(this.node);
|
||||
|
||||
if (this.maxPropertiesToShow) {
|
||||
basicProperties = basicProperties.slice(0, this.maxPropertiesToShow);
|
||||
}
|
||||
|
||||
this.properties = [...basicProperties];
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { CardViewModule } from '@alfresco/adf-core';
|
||||
|
||||
import { ContentMetadataComponent } from './content-metadata.component';
|
||||
import { ContentMetadataCardComponent } from './content-metadata-card.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MaterialModule,
|
||||
TranslateModule,
|
||||
FlexLayoutModule,
|
||||
CardViewModule
|
||||
],
|
||||
exports: [
|
||||
ContentMetadataComponent,
|
||||
ContentMetadataCardComponent
|
||||
],
|
||||
declarations: [
|
||||
ContentMetadataComponent,
|
||||
ContentMetadataCardComponent
|
||||
]
|
||||
})
|
||||
export class ContentMetadataModule {}
|
@ -0,0 +1,93 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { CardViewDateItemModel, CardViewTextItemModel, FileSizePipe } from '@alfresco/adf-core';
|
||||
|
||||
@Injectable()
|
||||
export class ContentMetadataService {
|
||||
|
||||
constructor(private fileSizePipe: FileSizePipe) {}
|
||||
|
||||
getBasicProperties(node: MinimalNodeEntryEntity) {
|
||||
return [
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.NAME',
|
||||
value: node.name,
|
||||
key: 'name',
|
||||
editable: true
|
||||
}),
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.TITLE',
|
||||
value: node.properties['cm:title'],
|
||||
key: 'properties.cm:title',
|
||||
editable: true
|
||||
}),
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.CREATOR',
|
||||
value: node.createdByUser.displayName,
|
||||
key: 'createdByUser.displayName',
|
||||
editable: false
|
||||
}),
|
||||
new CardViewDateItemModel({
|
||||
label: 'CORE.METADATA.BASIC.CREATED_DATE',
|
||||
value: node.createdAt,
|
||||
key: 'createdAt',
|
||||
editable: false
|
||||
}),
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.SIZE',
|
||||
value: node.content.sizeInBytes,
|
||||
key: 'content.sizeInBytes',
|
||||
pipes: [{ pipe: this.fileSizePipe }],
|
||||
editable: false
|
||||
}),
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.MODIFIER',
|
||||
value: node.modifiedByUser.displayName,
|
||||
key: 'modifiedByUser.displayName',
|
||||
editable: false
|
||||
}),
|
||||
new CardViewDateItemModel({
|
||||
label: 'CORE.METADATA.BASIC.MODIFIED_DATE',
|
||||
value: node.modifiedAt,
|
||||
key: 'modifiedAt',
|
||||
editable: false
|
||||
}),
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.MIMETYPE',
|
||||
value: node.content.mimeTypeName,
|
||||
key: 'content.mimeTypeName',
|
||||
editable: false
|
||||
}),
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.AUTHOR',
|
||||
value: node.properties['cm:author'],
|
||||
key: 'properties.cm:author',
|
||||
editable: true
|
||||
}),
|
||||
new CardViewTextItemModel({
|
||||
label: 'CORE.METADATA.BASIC.DESCRIPTION',
|
||||
value: node.properties['cm:description'],
|
||||
key: 'properties.cm:description',
|
||||
multiline: true,
|
||||
editable: true
|
||||
})
|
||||
];
|
||||
}
|
||||
}
|
18
lib/content-services/content-metadata/index.ts
Normal file
18
lib/content-services/content-metadata/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
21
lib/content-services/content-metadata/public-api.ts
Normal file
21
lib/content-services/content-metadata/public-api.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './content-metadata.component';
|
||||
export * from './content-metadata.service';
|
||||
|
||||
export * from './content-metadata.module';
|
@ -35,6 +35,7 @@ import { VersionManagerModule } from './version-manager';
|
||||
import { ContentNodeSelectorModule } from './content-node-selector';
|
||||
import { DialogModule } from './dialogs';
|
||||
import { DirectiveModule } from './directive';
|
||||
import { ContentMetadataModule } from './content-metadata';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -54,6 +55,7 @@ import { DirectiveModule } from './directive';
|
||||
BreadcrumbModule,
|
||||
VersionManagerModule,
|
||||
ContentNodeSelectorModule,
|
||||
ContentMetadataModule,
|
||||
DialogModule,
|
||||
DirectiveModule
|
||||
],
|
||||
@ -79,6 +81,7 @@ import { DirectiveModule } from './directive';
|
||||
BreadcrumbModule,
|
||||
VersionManagerModule,
|
||||
ContentNodeSelectorModule,
|
||||
ContentMetadataModule,
|
||||
DialogModule,
|
||||
DirectiveModule
|
||||
]
|
||||
|
@ -182,5 +182,19 @@
|
||||
},
|
||||
"PERMISSON": {
|
||||
"LACKOF": "You don't have the {{permission}} permission to {{action}} the {{type}}"
|
||||
},
|
||||
"METADATA": {
|
||||
"BASIC": {
|
||||
"NAME": "Name",
|
||||
"TITLE": "Title",
|
||||
"DESCRIPTION": "Description",
|
||||
"AUTHOR": "Author",
|
||||
"MIMETYPE": "Mimetype",
|
||||
"SIZE": "Size",
|
||||
"CREATOR": "Creator",
|
||||
"CREATED_DATE": "Created Date",
|
||||
"MODIFIER": "Modifier",
|
||||
"MODIFIED_DATE": "Modified Date"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ export * from './version-manager';
|
||||
export * from './content-node-selector';
|
||||
export * from './dialogs';
|
||||
export * from './directive';
|
||||
export * from './content-metadata';
|
||||
|
||||
export * from './mock';
|
||||
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
MatChipsModule,
|
||||
MatDialogModule,
|
||||
MatIconModule,
|
||||
MatCardModule,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatMenuModule,
|
||||
@ -37,6 +38,7 @@ export function modules() {
|
||||
MatChipsModule,
|
||||
MatDialogModule,
|
||||
MatIconModule,
|
||||
MatCardModule,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatProgressSpinnerModule,
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
@import '../dialogs/folder.dialog';
|
||||
|
||||
@import '../content-metadata/content-metadata.component';
|
||||
@import '../content-metadata/content-metadata-card.component';
|
||||
|
||||
@mixin adf-content-services-theme($theme) {
|
||||
@include adf-breadcrumb-theme($theme);
|
||||
@include adf-breadcrumb-dropdown-theme($theme);
|
||||
@ -21,4 +24,6 @@
|
||||
@include adf-search-control-theme($theme);
|
||||
@include adf-search-autocomplete-theme($theme);
|
||||
@include adf-dialog-theme($theme);
|
||||
@include adf-content-metadata-theme($theme);
|
||||
@include adf-content-metadata-card-theme($theme);
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ export class CardViewDateItemComponent implements OnInit {
|
||||
let momentDate = moment(newDateValue.value, this.SHOW_FORMAT, true);
|
||||
if (momentDate.isValid()) {
|
||||
this.valueDate = momentDate;
|
||||
this.cardViewUpdateService.update(this.property, {[this.property.key]: momentDate.toDate()});
|
||||
this.cardViewUpdateService.update(this.property, momentDate.toDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,11 @@
|
||||
</ng-template>
|
||||
</span>
|
||||
<span *ngIf="isEditble()">
|
||||
<div *ngIf="!inEdit" (click)="setEditMode(true)" class="adf-textitem-readonly" [attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key">
|
||||
<div *ngIf="!inEdit" (click)="setEditMode(true)" class="adf-textitem-readonly" [attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<span [attr.data-automation-id]="'card-textitem-value-' + property.key">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<mat-icon [attr.data-automation-id]="'card-textitem-edit-icon-' + property.key" class="adf-textitem-icon">create</mat-icon>
|
||||
<mat-icon fxFlex="0 0 auto" [attr.data-automation-id]="'card-textitem-edit-icon-' + property.key" class="adf-textitem-icon">create</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="inEdit" class="adf-textitem-editable">
|
||||
<mat-form-field floatPlaceholder="never" class="adf-input-container">
|
||||
|
@ -1,4 +1,5 @@
|
||||
@mixin adf-card-view-textitem-theme($theme) {
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf {
|
||||
&-textitem-icon {
|
||||
@ -6,9 +7,9 @@
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
top: 4px;
|
||||
padding-left: 8px;
|
||||
opacity: 0.5;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&-update-icon {
|
||||
@ -42,7 +43,7 @@
|
||||
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
border: 1px solid #EEE;
|
||||
border: 1px solid mat-color($foreground, text, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,13 +73,13 @@
|
||||
&-textitem-editable .mat-input-element {
|
||||
font-family: inherit;
|
||||
position: relative;
|
||||
padding-top: 3px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
&-textitem-editable .mat-input-element:focus {
|
||||
padding: 5px;
|
||||
left: -6px;
|
||||
top: -6px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-textitem-editable input.mat-input-element {
|
||||
|
@ -215,4 +215,19 @@ describe('CardViewTextItemComponent', () => {
|
||||
let updateInput = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-update-${component.property.key}"]`));
|
||||
updateInput.triggerEventHandler('click', null);
|
||||
});
|
||||
|
||||
it('should switch back to readonly mode after an update attempt', async(() => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.inEdit = true;
|
||||
component.editedValue = 'updated-value';
|
||||
fixture.detectChanges();
|
||||
|
||||
component.update();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.property.value).toBe(component.editedValue);
|
||||
expect(component.inEdit).toBeFalsy();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
@ -64,7 +64,9 @@ export class CardViewTextItemComponent implements OnChanges {
|
||||
}
|
||||
|
||||
update(): void {
|
||||
this.cardViewUpdateService.update(this.property, { [this.property.key]: this.editedValue });
|
||||
this.cardViewUpdateService.update(this.property, this.editedValue );
|
||||
this.property.value = this.editedValue;
|
||||
this.setEditMode(false);
|
||||
}
|
||||
|
||||
clicked(): void {
|
||||
|
@ -19,6 +19,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule, MatDatepickerModule, MatIconModule, MatInputModule, MatNativeDateModule } from '@angular/material';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CardViewContentProxyDirective } from './card-view-content-proxy.directive';
|
||||
@ -37,6 +38,7 @@ import { CardViewComponent } from './card-view.component';
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
FormsModule,
|
||||
FlexLayoutModule,
|
||||
TranslateModule
|
||||
],
|
||||
declarations: [
|
||||
|
@ -71,6 +71,20 @@
|
||||
"BACK": "Back",
|
||||
"APPLY": "APPLY",
|
||||
"NOT_VALID": "http(s)://host|ip:port(/path) not recognized, try a different URL."
|
||||
},
|
||||
"METADATA": {
|
||||
"BASIC": {
|
||||
"NAME": "Name",
|
||||
"TITLE": "Title",
|
||||
"DESCRIPTION": "Description",
|
||||
"AUTHOR": "Author",
|
||||
"MIMETYPE": "Mimetype",
|
||||
"SIZE": "Size",
|
||||
"CREATOR": "Creator",
|
||||
"CREATED_DATE": "Created Date",
|
||||
"MODIFIER": "Modifier",
|
||||
"MODIFIED_DATE": "Modified Date"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LOGIN": {
|
||||
@ -146,6 +160,12 @@
|
||||
"OF": "of"
|
||||
},
|
||||
"LOADING": "Loading",
|
||||
"UNKNOWN_FORMAT": "Couldn't load preview"
|
||||
"UNKNOWN_FORMAT": "Couldn't load preview",
|
||||
"SIDEBAR": {
|
||||
"METADATA": {
|
||||
"MORE_INFORMATION": "More information",
|
||||
"LESS_INFORMATION": "Less information"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
78
lib/core/models/card-view-textitem.model.spec.ts
Normal file
78
lib/core/models/card-view-textitem.model.spec.ts
Normal file
@ -0,0 +1,78 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PipeTransform } from '@angular/core';
|
||||
import { CardViewTextItemModel, CardViewTextItemProperties } from './card-view-textitem.model';
|
||||
|
||||
class TestPipe implements PipeTransform {
|
||||
transform(value: string, pipeParam: string): string {
|
||||
const paramPostFix = pipeParam ? `-${pipeParam}` : '';
|
||||
return `testpiped-${value}${paramPostFix}`;
|
||||
}
|
||||
}
|
||||
|
||||
describe('CardViewTextItemModel', () => {
|
||||
|
||||
let properties: CardViewTextItemProperties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
label: 'Tribe',
|
||||
value: 'Banuk',
|
||||
key: 'tribe'
|
||||
};
|
||||
});
|
||||
|
||||
describe('displayValue', () => {
|
||||
|
||||
it('should return the extension if file has it', () => {
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('Banuk');
|
||||
});
|
||||
|
||||
it('should apply a pipe on the value if it is present', () => {
|
||||
properties.pipes = [
|
||||
{ pipe: new TestPipe() }
|
||||
];
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('testpiped-Banuk');
|
||||
});
|
||||
|
||||
it('should apply a pipe on the value with parameters if those are present', () => {
|
||||
properties.pipes = [
|
||||
{ pipe: new TestPipe(), params: ['withParams'] }
|
||||
];
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('testpiped-Banuk-withParams');
|
||||
});
|
||||
|
||||
it('should apply more pipes on the value with parameters if those are present', () => {
|
||||
const pipe: PipeTransform = new TestPipe();
|
||||
properties.pipes = [
|
||||
{ pipe, params: ['1'] },
|
||||
{ pipe, params: ['2'] },
|
||||
{ pipe, params: ['3'] }
|
||||
];
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('testpiped-testpiped-testpiped-Banuk-1-2-3');
|
||||
});
|
||||
});
|
||||
});
|
@ -23,24 +23,41 @@
|
||||
* @returns {CardViewTextItemModel} .
|
||||
*/
|
||||
|
||||
import { PipeTransform } from '@angular/core';
|
||||
import { CardViewItem } from '../interface/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel, CardViewItemProperties } from './card-view-baseitem.model';
|
||||
|
||||
export interface CardViewTextItemPipeProperty {
|
||||
pipe: PipeTransform;
|
||||
params?: Array<any>;
|
||||
}
|
||||
export interface CardViewTextItemProperties extends CardViewItemProperties {
|
||||
multiline?: boolean;
|
||||
pipes?: Array<CardViewTextItemPipeProperty>;
|
||||
}
|
||||
export class CardViewTextItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'text';
|
||||
multiline: boolean;
|
||||
multiline?: boolean;
|
||||
pipes?: Array<CardViewTextItemPipeProperty>;
|
||||
|
||||
constructor(obj: CardViewTextItemProperties) {
|
||||
super(obj);
|
||||
this.multiline = !!obj.multiline ;
|
||||
this.pipes = obj.pipes || [];
|
||||
}
|
||||
|
||||
get displayValue() {
|
||||
return this.value;
|
||||
return this.applyPipes(this.value);
|
||||
}
|
||||
|
||||
private applyPipes(displayValue) {
|
||||
if (this.pipes.length) {
|
||||
displayValue = this.pipes.reduce((accumulator, { pipe, params }) => {
|
||||
return pipe.transform(accumulator, ...params);
|
||||
}, displayValue);
|
||||
}
|
||||
|
||||
return displayValue;
|
||||
}
|
||||
}
|
||||
|
94
lib/core/services/card-view-update.service.spec.ts
Normal file
94
lib/core/services/card-view-update.service.spec.ts
Normal file
@ -0,0 +1,94 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
|
||||
import { CardViewUpdateService, transformKeyToObject } from './card-view-update.service';
|
||||
|
||||
describe('CardViewUpdateService', () => {
|
||||
|
||||
describe('transformKeyToObject', () => {
|
||||
|
||||
it('should return the proper constructed value object for "dotless" keys', () => {
|
||||
const valueObject = transformKeyToObject('property-key', 'property-value');
|
||||
|
||||
expect(valueObject).toEqual({
|
||||
'property-key': 'property-value'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the proper constructed value object for dot contained keys', () => {
|
||||
const valueObject = transformKeyToObject('level:0.level:1.level:2.level:3', 'property-value');
|
||||
|
||||
expect(valueObject).toEqual({
|
||||
'level:0': {
|
||||
'level:1': {
|
||||
'level:2': {
|
||||
'level:3': 'property-value'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Service', () => {
|
||||
|
||||
let cardViewUpdateService: CardViewUpdateService;
|
||||
const property: CardViewBaseItemModel = <CardViewBaseItemModel> {
|
||||
label: 'property-label',
|
||||
value: 'property-value',
|
||||
key: 'property-key',
|
||||
default: 'property-default',
|
||||
editable: false,
|
||||
clickable: false
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
CardViewUpdateService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
cardViewUpdateService = TestBed.get(CardViewUpdateService);
|
||||
});
|
||||
|
||||
it('should send updated message with proper parameters', async(() => {
|
||||
|
||||
cardViewUpdateService.itemUpdated$.subscribe(
|
||||
( { target, changed } ) => {
|
||||
expect(target).toBe(property);
|
||||
expect(changed).toEqual({ 'property-key': 'changed-property-value' });
|
||||
}
|
||||
);
|
||||
cardViewUpdateService.update(property, 'changed-property-value');
|
||||
}));
|
||||
|
||||
it('should send clicked message with proper parameters', async(() => {
|
||||
|
||||
cardViewUpdateService.itemClicked$.subscribe(
|
||||
( { target } ) => {
|
||||
expect(target).toBe(property);
|
||||
}
|
||||
);
|
||||
cardViewUpdateService.clicked(property);
|
||||
}));
|
||||
});
|
||||
});
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { Observable, Subject } from 'rxjs/Rx';
|
||||
import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
|
||||
|
||||
export interface UpdateNotification {
|
||||
@ -28,26 +28,34 @@ export interface ClickNotification {
|
||||
target: any;
|
||||
}
|
||||
|
||||
export function transformKeyToObject(key: string, value): Object {
|
||||
const objectLevels: string[] = key.split('.').reverse();
|
||||
|
||||
return objectLevels.reduce<{}>((previousValue, currentValue) => {
|
||||
return { [currentValue]: previousValue};
|
||||
}, value);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CardViewUpdateService {
|
||||
|
||||
// Observable sources
|
||||
private itemUpdatedSource = new Subject<UpdateNotification>();
|
||||
private itemClickedSource = new Subject<ClickNotification>();
|
||||
|
||||
// Observable streams
|
||||
public itemUpdated$ = this.itemUpdatedSource.asObservable();
|
||||
public itemUpdated$ = <Observable<UpdateNotification>> this.itemUpdatedSource.asObservable();
|
||||
public itemClicked$ = <Observable<ClickNotification>> this.itemClickedSource.asObservable();
|
||||
|
||||
public itemClicked$: Subject<ClickNotification> = new Subject<ClickNotification>();
|
||||
|
||||
update(property: CardViewBaseItemModel, changed: any) {
|
||||
update(property: CardViewBaseItemModel, newValue: any) {
|
||||
this.itemUpdatedSource.next({
|
||||
target: property,
|
||||
changed
|
||||
changed: transformKeyToObject(property.key, newValue)
|
||||
});
|
||||
}
|
||||
|
||||
clicked(property: CardViewBaseItemModel) {
|
||||
this.itemClicked$.next({
|
||||
this.itemClickedSource.next({
|
||||
target: property
|
||||
});
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
@import '../login/components/login.component';
|
||||
@import '../datatable/components/datatable/datatable.component';
|
||||
@import '../form/components/widgets/form';
|
||||
@import '../viewer/components/viewer.component';
|
||||
|
||||
@mixin adf-core-theme($theme) {
|
||||
@include adf-form-theme($theme);
|
||||
@ -30,6 +31,7 @@
|
||||
@include adf-userinfo-theme($theme);
|
||||
@include adf-login-theme($theme);
|
||||
@include adf-datatable-theme($theme);
|
||||
@include adf-viewer-theme($theme);
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,10 +150,10 @@
|
||||
|
||||
<ng-container *ngIf="showSidebar && sidebarPosition !== 'left'">
|
||||
<div class="adf-viewer__sidebar adf-viewer__sidebar-right">
|
||||
<ng-content select="adf-viewer-sidebar"></ng-content>
|
||||
<ng-container *ngIf="!sidebar">
|
||||
<!-- todo: default info drawer -->
|
||||
<ng-container *ngIf="sidebarTemplate">
|
||||
<ng-container *ngTemplateOutlet="sidebarTemplate;context:sidebarTemplateContext"></ng-container>
|
||||
</ng-container>
|
||||
<ng-content *ngIf="!sidebarTemplate" select="adf-viewer-sidebar"></ng-content>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
@ -1,6 +1,9 @@
|
||||
$adf-viewer-background-color: #f5f5f5;
|
||||
@mixin adf-viewer-theme($theme) {
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$adf-viewer-background-color: mat-color($background, card);
|
||||
|
||||
@mixin full-screen() {
|
||||
.full-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $adf-viewer-background-color;
|
||||
@ -14,7 +17,7 @@ $adf-viewer-background-color: #f5f5f5;
|
||||
|
||||
&-container {
|
||||
.adf-viewer-layout-content {
|
||||
@include full-screen();
|
||||
@extend .full-screen;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@ -34,7 +37,7 @@ $adf-viewer-background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.adf-viewer-layout {
|
||||
@include full-screen();
|
||||
@extend .full-screen;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -44,7 +47,7 @@ $adf-viewer-background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.adf-viewer-content {
|
||||
@include full-screen();
|
||||
@extend .full-screen;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
@ -59,7 +62,7 @@ $adf-viewer-background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
&-inline-container {
|
||||
@include full-screen();
|
||||
@extend .full-screen;
|
||||
}
|
||||
|
||||
&-content-container {
|
||||
@ -87,9 +90,10 @@ $adf-viewer-background-color: #f5f5f5;
|
||||
&__sidebar {
|
||||
width: 350px;
|
||||
display: block;
|
||||
padding: 8px 0;
|
||||
padding: 0;
|
||||
background-color: #fafafa;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.27);
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.07);
|
||||
border-left: 1px solid mat-color($foreground, text, 0.07);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import {
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { BaseEvent } from '../../events';
|
||||
import { AlfrescoApiService, LogService, RenditionsService } from '../../services';
|
||||
|
||||
import { ViewerMoreActionsComponent } from './viewer-more-actions.component';
|
||||
import { ViewerOpenWithComponent } from './viewer-open-with.component';
|
||||
import { ViewerSidebarComponent } from './viewer-sidebar.component';
|
||||
@ -92,6 +91,9 @@ export class ViewerComponent implements OnDestroy, OnChanges {
|
||||
@Input()
|
||||
sidebarPosition = 'right';
|
||||
|
||||
@Input()
|
||||
sidebarTemplate: TemplateRef<any> = null;
|
||||
|
||||
@Output()
|
||||
goBack = new EventEmitter<BaseEvent<any>>();
|
||||
|
||||
@ -114,6 +116,7 @@ export class ViewerComponent implements OnDestroy, OnChanges {
|
||||
downloadUrl: string = null;
|
||||
fileName = 'document';
|
||||
isLoading = false;
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
extensionTemplates: { template: TemplateRef<any>, isVisible: boolean }[] = [];
|
||||
externalExtensions: string[] = [];
|
||||
@ -121,6 +124,7 @@ export class ViewerComponent implements OnDestroy, OnChanges {
|
||||
otherMenu: any;
|
||||
extension: string;
|
||||
mimeType: string;
|
||||
sidebarTemplateContext: { node: MinimalNodeEntryEntity } = { node: null };
|
||||
|
||||
private extensions = {
|
||||
image: ['png', 'jpg', 'jpeg', 'gif', 'bpm'],
|
||||
@ -203,6 +207,7 @@ export class ViewerComponent implements OnDestroy, OnChanges {
|
||||
}
|
||||
|
||||
this.extensionChange.emit(this.extension);
|
||||
this.sidebarTemplateContext.node = data;
|
||||
this.scrollTop();
|
||||
resolve();
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user