diff --git a/demo-shell/src/app/components/file-view/file-view.component.html b/demo-shell/src/app/components/file-view/file-view.component.html index e7fd47a873..7625eb1e0f 100644 --- a/demo-shell/src/app/components/file-view/file-view.component.html +++ b/demo-shell/src/app/components/file-view/file-view.component.html @@ -1,36 +1,52 @@ - - + + + + + - + + + + Versions go here... + + + + + - + + - + + + + + - + \ No newline at end of file diff --git a/docs/card-view.component.md b/docs/card-view.component.md index 19ead327be..abfb32e8e1 100644 --- a/docs/card-view.component.md +++ b/docs/card-view.component.md @@ -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 = ; +const myWonderfulPipe2: PipeTransform = ; + +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 diff --git a/lib/content-services/content-metadata/content-metadata-card.component.html b/lib/content-services/content-metadata/content-metadata-card.component.html new file mode 100644 index 0000000000..5dea07f211 --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata-card.component.html @@ -0,0 +1,25 @@ + + + + + +
+ + +
+ +
+
diff --git a/lib/content-services/content-metadata/content-metadata-card.component.scss b/lib/content-services/content-metadata/content-metadata-card.component.scss new file mode 100644 index 0000000000..9426378da6 --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata-card.component.scss @@ -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); + } + } + } + } +} \ No newline at end of file diff --git a/lib/content-services/content-metadata/content-metadata-card.component.ts b/lib/content-services/content-metadata/content-metadata-card.component.ts new file mode 100644 index 0000000000..5021a57c01 --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata-card.component.ts @@ -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; + } +} diff --git a/lib/content-services/content-metadata/content-metadata.component.html b/lib/content-services/content-metadata/content-metadata.component.html new file mode 100644 index 0000000000..b9eae6c36d --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata.component.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/lib/content-services/content-metadata/content-metadata.component.scss b/lib/content-services/content-metadata/content-metadata.component.scss new file mode 100644 index 0000000000..25222c4a69 --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata.component.scss @@ -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); + } + } + } + } +} diff --git a/lib/content-services/content-metadata/content-metadata.component.ts b/lib/content-services/content-metadata/content-metadata.component.ts new file mode 100644 index 0000000000..cb61e01f6b --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata.component.ts @@ -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 { + 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]; + } +} diff --git a/lib/content-services/content-metadata/content-metadata.module.ts b/lib/content-services/content-metadata/content-metadata.module.ts new file mode 100644 index 0000000000..5d4e76b863 --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata.module.ts @@ -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 {} diff --git a/lib/content-services/content-metadata/content-metadata.service.ts b/lib/content-services/content-metadata/content-metadata.service.ts new file mode 100644 index 0000000000..06d05cd540 --- /dev/null +++ b/lib/content-services/content-metadata/content-metadata.service.ts @@ -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 + }) + ]; + } +} diff --git a/lib/content-services/content-metadata/index.ts b/lib/content-services/content-metadata/index.ts new file mode 100644 index 0000000000..4c6ac1d58f --- /dev/null +++ b/lib/content-services/content-metadata/index.ts @@ -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'; diff --git a/lib/content-services/content-metadata/public-api.ts b/lib/content-services/content-metadata/public-api.ts new file mode 100644 index 0000000000..2c339b3ab5 --- /dev/null +++ b/lib/content-services/content-metadata/public-api.ts @@ -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'; diff --git a/lib/content-services/content.module.ts b/lib/content-services/content.module.ts index ba12ba4b14..fc19e6f992 100644 --- a/lib/content-services/content.module.ts +++ b/lib/content-services/content.module.ts @@ -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 ] diff --git a/lib/content-services/i18n/en.json b/lib/content-services/i18n/en.json index 989a0c567e..a29c74d08c 100644 --- a/lib/content-services/i18n/en.json +++ b/lib/content-services/i18n/en.json @@ -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" + } } } diff --git a/lib/content-services/index.ts b/lib/content-services/index.ts index 181e54b6ae..49cc0206d6 100644 --- a/lib/content-services/index.ts +++ b/lib/content-services/index.ts @@ -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'; diff --git a/lib/content-services/material.module.ts b/lib/content-services/material.module.ts index e7a7bc484e..9c79ff8008 100644 --- a/lib/content-services/material.module.ts +++ b/lib/content-services/material.module.ts @@ -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, diff --git a/lib/content-services/styles/index.scss b/lib/content-services/styles/index.scss index 24caf18420..5bae304533 100644 --- a/lib/content-services/styles/index.scss +++ b/lib/content-services/styles/index.scss @@ -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); } diff --git a/lib/core/card-view/card-view-dateitem.component.ts b/lib/core/card-view/card-view-dateitem.component.ts index 2c2742a9e0..9902dd019f 100644 --- a/lib/core/card-view/card-view-dateitem.component.ts +++ b/lib/core/card-view/card-view-dateitem.component.ts @@ -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()); } } } diff --git a/lib/core/card-view/card-view-textitem.component.html b/lib/core/card-view/card-view-textitem.component.html index 5637fa3424..c8fcf87199 100644 --- a/lib/core/card-view/card-view-textitem.component.html +++ b/lib/core/card-view/card-view-textitem.component.html @@ -11,11 +11,11 @@ -
+
{{ property.displayValue }} - create + create
diff --git a/lib/core/card-view/card-view-textitem.component.scss b/lib/core/card-view/card-view-textitem.component.scss index 11a585165a..a803682fad 100644 --- a/lib/core/card-view/card-view-textitem.component.scss +++ b/lib/core/card-view/card-view-textitem.component.scss @@ -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 { diff --git a/lib/core/card-view/card-view-textitem.component.spec.ts b/lib/core/card-view/card-view-textitem.component.spec.ts index d3ac3b37f7..ff73e6fdbc 100644 --- a/lib/core/card-view/card-view-textitem.component.spec.ts +++ b/lib/core/card-view/card-view-textitem.component.spec.ts @@ -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(); + }); + })); }); diff --git a/lib/core/card-view/card-view-textitem.component.ts b/lib/core/card-view/card-view-textitem.component.ts index 955e5a8fe6..6e9aea1826 100644 --- a/lib/core/card-view/card-view-textitem.component.ts +++ b/lib/core/card-view/card-view-textitem.component.ts @@ -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 { diff --git a/lib/core/card-view/card-view.module.ts b/lib/core/card-view/card-view.module.ts index e062e3960d..4941131289 100644 --- a/lib/core/card-view/card-view.module.ts +++ b/lib/core/card-view/card-view.module.ts @@ -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: [ diff --git a/lib/core/i18n/en.json b/lib/core/i18n/en.json index d285fe72d7..7cd077d03f 100644 --- a/lib/core/i18n/en.json +++ b/lib/core/i18n/en.json @@ -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" + } + } } } diff --git a/lib/core/models/card-view-textitem.model.spec.ts b/lib/core/models/card-view-textitem.model.spec.ts new file mode 100644 index 0000000000..240dc17803 --- /dev/null +++ b/lib/core/models/card-view-textitem.model.spec.ts @@ -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'); + }); + }); +}); diff --git a/lib/core/models/card-view-textitem.model.ts b/lib/core/models/card-view-textitem.model.ts index f9d8f1156b..48eea977dc 100644 --- a/lib/core/models/card-view-textitem.model.ts +++ b/lib/core/models/card-view-textitem.model.ts @@ -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; +} export interface CardViewTextItemProperties extends CardViewItemProperties { multiline?: boolean; + pipes?: Array; } export class CardViewTextItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel { type: string = 'text'; - multiline: boolean; + multiline?: boolean; + pipes?: Array; 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; + } } diff --git a/lib/core/services/card-view-update.service.spec.ts b/lib/core/services/card-view-update.service.spec.ts new file mode 100644 index 0000000000..d57bd863bc --- /dev/null +++ b/lib/core/services/card-view-update.service.spec.ts @@ -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 = { + 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); + })); + }); +}); diff --git a/lib/core/services/card-view-update.service.ts b/lib/core/services/card-view-update.service.ts index e2e2be4bb2..107a601cfe 100644 --- a/lib/core/services/card-view-update.service.ts +++ b/lib/core/services/card-view-update.service.ts @@ -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(); + private itemClickedSource = new Subject(); // Observable streams - public itemUpdated$ = this.itemUpdatedSource.asObservable(); + public itemUpdated$ = > this.itemUpdatedSource.asObservable(); + public itemClicked$ = > this.itemClickedSource.asObservable(); - public itemClicked$: Subject = new Subject(); - - 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 }); } diff --git a/lib/core/styles/_index.scss b/lib/core/styles/_index.scss index 1f6b98dd8f..745a318548 100644 --- a/lib/core/styles/_index.scss +++ b/lib/core/styles/_index.scss @@ -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); } diff --git a/lib/core/viewer/components/viewer.component.html b/lib/core/viewer/components/viewer.component.html index 1fc190ad84..378008bab9 100644 --- a/lib/core/viewer/components/viewer.component.html +++ b/lib/core/viewer/components/viewer.component.html @@ -150,10 +150,10 @@
- - - + + +
diff --git a/lib/core/viewer/components/viewer.component.scss b/lib/core/viewer/components/viewer.component.scss index 7ae9ac19c5..cb11d12633 100644 --- a/lib/core/viewer/components/viewer.component.scss +++ b/lib/core/viewer/components/viewer.component.scss @@ -1,95 +1,99 @@ -$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() { - width: 100%; - height: 100%; - background-color: $adf-viewer-background-color; -} - -.adf-viewer { - - &__mimeicon { - vertical-align: middle; + .full-screen { + width: 100%; + height: 100%; + background-color: $adf-viewer-background-color; } - &-container { - .adf-viewer-layout-content { - @include full-screen(); - position: relative; - overflow-y: auto; - overflow-x: hidden; - z-index: 1; + .adf-viewer { - display: flex; - flex-direction: row; - flex-wrap: wrap; - flex: 1; + &__mimeicon { + vertical-align: middle; + } + + &-container { + .adf-viewer-layout-content { + @extend .full-screen; + position: relative; + overflow-y: auto; + overflow-x: hidden; + z-index: 1; - & > div { display: flex; - flex-flow: row wrap; - margin: 0 auto; - align-items: stretch; + flex-direction: row; + flex-wrap: wrap; + flex: 1; + + & > div { + display: flex; + flex-flow: row wrap; + margin: 0 auto; + align-items: stretch; + } + } + + .adf-viewer-layout { + @extend .full-screen; + + display: flex; + flex-direction: row; + overflow-y: auto; + overflow-x: hidden; + position: relative; + } + + .adf-viewer-content { + @extend .full-screen; + flex: 1; } } - .adf-viewer-layout { - @include full-screen(); + &-overlay-container { + .adf-viewer-content { + position: fixed; + top: 0px; + left: 0px; + z-index: 1000; + } + } + &-inline-container { + @extend .full-screen; + } + + &-content-container { display: flex; - flex-direction: row; - overflow-y: auto; - overflow-x: hidden; - position: relative; + justify-content: center; } - .adf-viewer-content { - @include full-screen(); - flex: 1; + &-unknown-content { + align-items: center; + display: flex; } - } - &-overlay-container { - .adf-viewer-content { - position: fixed; - top: 0px; - left: 0px; - z-index: 1000; + &__loading-screen { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + height: 85vh; + + .mat-spinner { + margin: 0 auto; + } } - } - &-inline-container { - @include full-screen(); - } - - &-content-container { - display: flex; - justify-content: center; - } - - &-unknown-content { - align-items: center; - display: flex; - } - - &__loading-screen { - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - height: 85vh; - - .mat-spinner { - margin: 0 auto; + &__sidebar { + width: 350px; + display: block; + padding: 0; + background-color: #fafafa; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.27); + border-left: 1px solid mat-color($foreground, text, 0.07); } } - - &__sidebar { - width: 350px; - display: block; - padding: 8px 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); - } } diff --git a/lib/core/viewer/components/viewer.component.ts b/lib/core/viewer/components/viewer.component.ts index 79b3aa0115..0501603f63 100644 --- a/lib/core/viewer/components/viewer.component.ts +++ b/lib/core/viewer/components/viewer.component.ts @@ -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 = null; + @Output() goBack = new EventEmitter>(); @@ -114,6 +116,7 @@ export class ViewerComponent implements OnDestroy, OnChanges { downloadUrl: string = null; fileName = 'document'; isLoading = false; + node: MinimalNodeEntryEntity; extensionTemplates: { template: TemplateRef, 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(); },