mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-10 11:33:04 +00:00
46063: working CSS truncation, but were switching to JS again...
This commit is contained in:
10
src/app/shared/animations/overlay.ts
Normal file
10
src/app/shared/animations/overlay.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { animate, state, transition, trigger, style } from '@angular/animations';
|
||||||
|
|
||||||
|
export const overlay = trigger('overlay', [
|
||||||
|
|
||||||
|
state('show', style({ opacity: 0.5 })),
|
||||||
|
|
||||||
|
state('hide', style({ opacity: 0 })),
|
||||||
|
|
||||||
|
transition('show <=> hide', animate(250))
|
||||||
|
]);
|
@@ -10,8 +10,8 @@
|
|||||||
(sortDirectionChange)="onSortDirectionChange($event)"
|
(sortDirectionChange)="onSortDirectionChange($event)"
|
||||||
(sortFieldChange)="onSortFieldChange($event)"
|
(sortFieldChange)="onSortFieldChange($event)"
|
||||||
(paginationChange)="onPaginationChange($event)">
|
(paginationChange)="onPaginationChange($event)">
|
||||||
<div class="row mt-2" *ngIf="objects?.hasSucceeded" @fadeIn>
|
<div class="card-columns" *ngIf="objects?.hasSucceeded" @fadeIn>
|
||||||
<div class="col-lg-4 col-sm-6 col-xs-12 "
|
<div
|
||||||
*ngFor="let object of objects?.payload?.page">
|
*ngFor="let object of objects?.payload?.page">
|
||||||
<ds-wrapper-grid-element [object]="object"></ds-wrapper-grid-element>
|
<ds-wrapper-grid-element [object]="object"></ds-wrapper-grid-element>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,27 +1,24 @@
|
|||||||
@import '../../../styles/variables';
|
@import '../../../styles/variables';
|
||||||
|
@import '../../../styles/mixins';
|
||||||
|
|
||||||
ds-wrapper-grid-element ::ng-deep {
|
ds-wrapper-grid-element ::ng-deep {
|
||||||
div.thumbnail > img {
|
div.thumbnail > img {
|
||||||
height: $card-thumbnail-height;
|
height: $card-thumbnail-height;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.card-title {
|
|
||||||
line-height: $headings-line-height;
|
|
||||||
height: ($headings-line-height*3) +em;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.item-abstract {
|
|
||||||
line-height: $line-height-base;
|
|
||||||
height: ($line-height-base*5)+em;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.item-authors{
|
|
||||||
line-height: $line-height-base;
|
|
||||||
height: ($line-height-base*1.5)+em;
|
|
||||||
}
|
|
||||||
div.card {
|
div.card {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-columns {
|
||||||
|
@include media-breakpoint-only(lg) {
|
||||||
|
column-count: 3;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-only(sm) {
|
||||||
|
column-count: 2;
|
||||||
|
}
|
||||||
|
@include media-breakpoint-only(xs) {
|
||||||
|
column-count: 1;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,34 +1,37 @@
|
|||||||
<div class="card">
|
<ds-truncatable [id]="dso.id">
|
||||||
<a [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
<div class="card">
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail()">
|
<a [@slide]="(isCollapsed() | async)? 'expanded' : 'collapsed'"
|
||||||
</ds-grid-thumbnail>
|
[routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
||||||
</a>
|
<div>
|
||||||
<div class="card-body">
|
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail()">
|
||||||
<h4 class="card-title" [innerHTML]="dso.findMetadata('dc.title')"></h4>
|
</ds-grid-thumbnail>
|
||||||
|
<div [@overlay]="(isCollapsed() | async)? 'hide' : 'show'"
|
||||||
|
class="thumbnail-overlay"></div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="card-body">
|
||||||
|
<ds-truncatable-part [id]="dso.id" [minLines]="1" [maxLines]="3" type="h4">
|
||||||
|
<h4 class="card-title" [innerHTML]="dso.findMetadata('dc.title')"></h4>
|
||||||
|
</ds-truncatable-part>
|
||||||
|
<p *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
|
||||||
|
class="item-authors card-text text-muted">
|
||||||
|
<ds-truncatable-part [id]="dso.id" [minLines]="1" [maxLines]="3">
|
||||||
|
<span *ngIf="dso.findMetadata('dc.date.issued')" class="item-list-date">{{dso.findMetadata("dc.date.issued")}}</span>
|
||||||
|
<span *ngFor="let authorMd of dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);">,
|
||||||
|
<span [innerHTML]="authorMd.value"></span>
|
||||||
|
</span>
|
||||||
|
</ds-truncatable-part>
|
||||||
|
</p>
|
||||||
|
<ds-truncatable-part [id]="dso.id" [minLines]="3" [maxLines]="15">
|
||||||
|
<p class="item-abstract card-text"
|
||||||
|
[innerHTML]="getFirstValue('dc.description.abstract')">
|
||||||
|
</p>
|
||||||
|
</ds-truncatable-part>
|
||||||
|
<div class="text-center">
|
||||||
|
<a [routerLink]="['/items/' + dso.id]"
|
||||||
|
class="lead btn btn-primary viewButton">View</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
|
|
||||||
class="item-authors card-text text-muted">
|
|
||||||
<span
|
|
||||||
*ngFor="let authorMd of dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let first=first;">
|
|
||||||
<span *ngIf="first" [innerHTML]="authorMd.value">
|
|
||||||
<span
|
|
||||||
*ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length>1">, ...</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span *ngIf="dso.findMetadata('dc.date.issued')"
|
|
||||||
class="item-list-date">
|
|
||||||
<span *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length>1">,</span>
|
|
||||||
{{dso.findMetadata("dc.date.issued")}}</span>
|
|
||||||
|
|
||||||
|
|
||||||
</p>
|
|
||||||
<p class="item-abstract card-text" [innerHTML]="getFirstValue('dc.description.abstract') | dsTruncate:[200]">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="text-center">
|
|
||||||
<a [routerLink]="['/items/' + dso.id]" class="lead btn btn-primary viewButton">View</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ds-truncatable>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
@@ -1,2 +1,14 @@
|
|||||||
@import '../../../../../styles/variables';
|
@import '../../../../../styles/variables';
|
||||||
|
|
||||||
|
.card {
|
||||||
|
a > div {
|
||||||
|
position: relative;
|
||||||
|
.thumbnail-overlay {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: map-get($theme-colors, primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -5,11 +5,14 @@ import { SearchResultGridElementComponent } from '../search-result-grid-element.
|
|||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
|
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
|
||||||
import { ViewMode } from '../../../../+search-page/search-options.model';
|
import { ViewMode } from '../../../../+search-page/search-options.model';
|
||||||
|
import { slide } from '../../../animations/slide';
|
||||||
|
import { overlay } from '../../../animations/overlay';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-search-result-grid-element',
|
selector: 'ds-item-search-result-grid-element',
|
||||||
styleUrls: ['../search-result-grid-element.component.scss', 'item-search-result-grid-element.component.scss'],
|
styleUrls: ['../search-result-grid-element.component.scss', 'item-search-result-grid-element.component.scss'],
|
||||||
templateUrl: 'item-search-result-grid-element.component.html'
|
templateUrl: 'item-search-result-grid-element.component.html',
|
||||||
|
animations: [slide, overlay],
|
||||||
})
|
})
|
||||||
|
|
||||||
@renderElementsFor(ItemSearchResult, ViewMode.Grid)
|
@renderElementsFor(ItemSearchResult, ViewMode.Grid)
|
||||||
|
@@ -6,6 +6,8 @@ import { Metadatum } from '../../../core/shared/metadatum.model';
|
|||||||
import { isEmpty, hasNoValue } from '../../empty.util';
|
import { isEmpty, hasNoValue } from '../../empty.util';
|
||||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||||
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
||||||
|
import { TruncatableService } from '../../truncatable/truncatable.service';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-result-grid-element',
|
selector: 'ds-search-result-grid-element',
|
||||||
@@ -15,7 +17,7 @@ import { ListableObject } from '../../object-collection/shared/listable-object.m
|
|||||||
export class SearchResultGridElementComponent<T extends SearchResult<K>, K extends DSpaceObject> extends AbstractListableElementComponent<T> {
|
export class SearchResultGridElementComponent<T extends SearchResult<K>, K extends DSpaceObject> extends AbstractListableElementComponent<T> {
|
||||||
dso: K;
|
dso: K;
|
||||||
|
|
||||||
public constructor(@Inject('objectElementProvider') public gridable: ListableObject) {
|
public constructor(@Inject('objectElementProvider') public gridable: ListableObject, private truncatableService: TruncatableService) {
|
||||||
super(gridable);
|
super(gridable);
|
||||||
this.dso = this.object.dspaceObject;
|
this.dso = this.object.dspaceObject;
|
||||||
}
|
}
|
||||||
@@ -44,7 +46,7 @@ export class SearchResultGridElementComponent<T extends SearchResult<K>, K exten
|
|||||||
this.object.hitHighlights.some(
|
this.object.hitHighlights.some(
|
||||||
(md: Metadatum) => {
|
(md: Metadatum) => {
|
||||||
if (key === md.key) {
|
if (key === md.key) {
|
||||||
result = md.value;
|
result = md.value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,4 +56,8 @@ export class SearchResultGridElementComponent<T extends SearchResult<K>, K exten
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCollapsed(): Observable<boolean> {
|
||||||
|
return this.truncatableService.isCollapsed(this.dso.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
(sortDirectionChange)="onSortDirectionChange($event)"
|
(sortDirectionChange)="onSortDirectionChange($event)"
|
||||||
(sortFieldChange)="onSortFieldChange($event)"
|
(sortFieldChange)="onSortFieldChange($event)"
|
||||||
(paginationChange)="onPaginationChange($event)">
|
(paginationChange)="onPaginationChange($event)">
|
||||||
<ul *ngIf="objects?.hasSucceeded"> <!--class="list-unstyled"-->
|
<ul *ngIf="objects?.hasSucceeded" class="list-unstyled">
|
||||||
<li *ngFor="let object of objects?.payload?.page">
|
<li *ngFor="let object of objects?.payload?.page">
|
||||||
<ds-wrapper-list-element [object]="object"></ds-wrapper-list-element>
|
<ds-wrapper-list-element [object]="object"></ds-wrapper-list-element>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -1,14 +1,28 @@
|
|||||||
<a [routerLink]="['/items/' + dso.id]" class="lead" [innerHTML]="getFirstValue('dc.title')"></a>
|
<ds-truncatable [id]="dso.id">
|
||||||
<div>
|
<ds-truncatable-part [id]="dso.id" [minLines]="1" [maxLines]="5" type="title">
|
||||||
|
<a [routerLink]="['/items/' + dso.id]" class="lead"
|
||||||
|
[innerHTML]="getFirstValue('dc.title')"></a>
|
||||||
|
</ds-truncatable-part>
|
||||||
|
<div>
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<span *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);" class="item-list-authors">
|
|
||||||
<span *ngFor="let author of getValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
|
<ds-truncatable-part [id]="dso.id" [minLines]="1" [maxLines]="5">
|
||||||
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
|
(<span *ngIf="dso.findMetadata('dc.publisher')" class="item-list-publisher"
|
||||||
</span>
|
[innerHTML]="getFirstValue('dc.publisher')">, </span><span
|
||||||
</span>
|
*ngIf="dso.findMetadata('dc.date.issued')" class="item-list-date"
|
||||||
(<span *ngIf="dso.findMetadata('dc.publisher')" class="item-list-publisher" [innerHTML]="getFirstValue('dc.publisher')">, </span><span *ngIf="dso.findMetadata('dc.date.issued')" class="item-list-date" [innerHTML]="getFirstValue('dc.date.issued')"></span>)
|
[innerHTML]="getFirstValue('dc.date.issued')"></span>)
|
||||||
|
<span *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);"
|
||||||
|
class="item-list-authors">
|
||||||
|
<span *ngFor="let author of getValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
|
||||||
|
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</ds-truncatable-part>
|
||||||
</span>
|
</span>
|
||||||
<div *ngIf="dso.findMetadata('dc.description.abstract')" class="item-list-abstract">
|
<div *ngIf="dso.findMetadata('dc.description.abstract')" class="item-list-abstract">
|
||||||
<ds-truncatable [id]="dso.id" [minLines]="3" [maxLines]="15" [content]="getFirstValue('dc.description.abstract')"></ds-truncatable>
|
<ds-truncatable-part [id]="dso.id" [minLines]="3" [maxLines]="15"><span
|
||||||
|
[innerHTML]="getFirstValue('dc.description.abstract')"></span>
|
||||||
|
</ds-truncatable-part>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ds-truncatable>
|
@@ -43,6 +43,7 @@ import { VarDirective } from './utils/var.directive';
|
|||||||
import { TruncatePipe } from './utils/truncate.pipe';
|
import { TruncatePipe } from './utils/truncate.pipe';
|
||||||
import { TruncatableComponent } from './truncatable/truncatable.component';
|
import { TruncatableComponent } from './truncatable/truncatable.component';
|
||||||
import { TruncatableService } from './truncatable/truncatable.service';
|
import { TruncatableService } from './truncatable/truncatable.service';
|
||||||
|
import { TruncatablePartComponent } from './truncatable/truncatable-part/truncatable-part.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||||
@@ -82,7 +83,8 @@ const COMPONENTS = [
|
|||||||
GridThumbnailComponent,
|
GridThumbnailComponent,
|
||||||
WrapperListElementComponent,
|
WrapperListElementComponent,
|
||||||
ViewModeSwitchComponent,
|
ViewModeSwitchComponent,
|
||||||
TruncatableComponent
|
TruncatableComponent,
|
||||||
|
TruncatablePartComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
<div class="clamp-{{lines}} {{type}}">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
@@ -0,0 +1,42 @@
|
|||||||
|
@import '../../../../styles/variables';
|
||||||
|
@import '../../../../styles/mixins';
|
||||||
|
|
||||||
|
@mixin clamp($lines, $size-factor: 1, $line-height: $line-height-base) {
|
||||||
|
$height: $line-height * $font-size-base * $size-factor;
|
||||||
|
max-height: $lines * $height;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: $line-height;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
&:after {
|
||||||
|
content: "...";
|
||||||
|
color: $link-color;
|
||||||
|
text-align: right;
|
||||||
|
position: absolute;
|
||||||
|
padding-right: 15px;
|
||||||
|
top: ($lines - 1) * $height;
|
||||||
|
right: 0;
|
||||||
|
width: 30%;
|
||||||
|
min-width: 75px;
|
||||||
|
max-width: 150px;
|
||||||
|
height: $height;
|
||||||
|
background: linear-gradient(to right, rgba(255, 255, 255, 0), $body-bg 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$h4-factor: strip-unit($h4-font-size);
|
||||||
|
@for $i from 1 through 15 {
|
||||||
|
.clamp-#{$i} {
|
||||||
|
@include clamp($i);
|
||||||
|
&.title {
|
||||||
|
@include clamp($i, 1.25);
|
||||||
|
}
|
||||||
|
&.h4 {
|
||||||
|
@include clamp($i, $h4-factor, $headings-line-height);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@@ -0,0 +1,42 @@
|
|||||||
|
import {
|
||||||
|
Component, Input, OnDestroy, OnInit, ElementRef, ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
import { TruncatableService } from '../truncatable.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-truncatable-part',
|
||||||
|
templateUrl: './truncatable-part.component.html',
|
||||||
|
styleUrls: ['./truncatable-part.component.scss']
|
||||||
|
})
|
||||||
|
|
||||||
|
export class TruncatablePartComponent implements OnInit, OnDestroy {
|
||||||
|
@Input() minLines: number;
|
||||||
|
@Input() maxLines: number;
|
||||||
|
@Input() initialExpand = false;
|
||||||
|
@Input() id: string;
|
||||||
|
@Input() type: string;
|
||||||
|
private lines: number;
|
||||||
|
private sub;
|
||||||
|
|
||||||
|
public constructor(private service: TruncatableService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.setLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setLines() {
|
||||||
|
this.sub = this.service.isCollapsed(this.id).subscribe((collapsed: boolean) => {
|
||||||
|
if (collapsed) {
|
||||||
|
this.lines = this.minLines;
|
||||||
|
} else {
|
||||||
|
this.lines = this.maxLines;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -11,6 +11,8 @@ import { type } from '../ngrx/type';
|
|||||||
*/
|
*/
|
||||||
export const TruncatableActionTypes = {
|
export const TruncatableActionTypes = {
|
||||||
TOGGLE: type('dspace/truncatable/TOGGLE'),
|
TOGGLE: type('dspace/truncatable/TOGGLE'),
|
||||||
|
COLLAPSE: type('dspace/truncatable/COLLAPSE'),
|
||||||
|
EXPAND: type('dspace/truncatable/EXPAND'),
|
||||||
};
|
};
|
||||||
|
|
||||||
export class TruncatableAction implements Action {
|
export class TruncatableAction implements Action {
|
||||||
@@ -26,4 +28,12 @@ export class TruncatableToggleAction extends TruncatableAction {
|
|||||||
type = TruncatableActionTypes.TOGGLE;
|
type = TruncatableActionTypes.TOGGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TruncatableCollapseAction extends TruncatableAction {
|
||||||
|
type = TruncatableActionTypes.COLLAPSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TruncatableExpandAction extends TruncatableAction {
|
||||||
|
type = TruncatableActionTypes.EXPAND;
|
||||||
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
/* tslint:enable:max-classes-per-file */
|
||||||
|
@@ -1 +1,3 @@
|
|||||||
<div class="clamp-{{lines}}" [innerHTML]="content" (click)="toggleCollapse()"></div>
|
<div (click)="toggle()" (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
@@ -1,41 +0,0 @@
|
|||||||
@import '../../../styles/_variables.scss';
|
|
||||||
@import '../../../styles/_mixins.scss';
|
|
||||||
|
|
||||||
@mixin clamp($lines) {
|
|
||||||
max-height: $lines * $line-height-base * $font-size-base;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
line-height: $line-height-base;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "...";
|
|
||||||
color: $link-color;
|
|
||||||
text-align: right;
|
|
||||||
position: absolute;
|
|
||||||
padding-right: 15px;
|
|
||||||
top: ($lines - 1) * $line-height-base * $font-size-base;
|
|
||||||
right: 0;
|
|
||||||
width: 30%;
|
|
||||||
min-width: 75px;
|
|
||||||
max-width: 150px;
|
|
||||||
height: $line-height-base * $font-size-base;
|
|
||||||
background: linear-gradient(to right, rgba(255, 255, 255, 0), $body-bg 50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
.clamp-1 {
|
|
||||||
@include clamp(1);
|
|
||||||
}
|
|
||||||
.clamp-2 {
|
|
||||||
@include clamp(2);
|
|
||||||
}
|
|
||||||
.clamp-3 {
|
|
||||||
@include clamp(3);
|
|
||||||
}
|
|
||||||
.clamp-15 {
|
|
||||||
@include clamp(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -9,34 +9,34 @@ import { TruncatableService } from './truncatable.service';
|
|||||||
styleUrls: ['./truncatable.component.scss']
|
styleUrls: ['./truncatable.component.scss']
|
||||||
})
|
})
|
||||||
export class TruncatableComponent {
|
export class TruncatableComponent {
|
||||||
@Input() minLines: number;
|
|
||||||
@Input() maxLines: number;
|
|
||||||
@Input() initialExpand = false;
|
@Input() initialExpand = false;
|
||||||
@Input() id: string;
|
@Input() id: string;
|
||||||
@Input() content;
|
@Input() onHover = false;
|
||||||
private lines: number;
|
|
||||||
|
|
||||||
public constructor(private service: TruncatableService) {
|
public constructor(private service: TruncatableService) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.initialExpand) {
|
if (this.initialExpand) {
|
||||||
this.service.toggle(this.id);
|
this.service.expand(this.id);
|
||||||
}
|
|
||||||
this.setLines();
|
|
||||||
}
|
|
||||||
|
|
||||||
public toggleCollapse() {
|
|
||||||
this.service.toggle(this.id);
|
|
||||||
this.setLines();
|
|
||||||
}
|
|
||||||
|
|
||||||
private setLines() {
|
|
||||||
if (this.service.isCollapsed(this.id)) {
|
|
||||||
this.lines = this.minLines;
|
|
||||||
} else {
|
} else {
|
||||||
this.lines = this.maxLines;
|
this.service.collapse(this.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hoverCollapse() {
|
||||||
|
if (this.onHover) {
|
||||||
|
this.service.collapse(this.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public hoverExpand() {
|
||||||
|
if (this.onHover) {
|
||||||
|
this.service.expand(this.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggle() {
|
||||||
|
this.service.toggle(this.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,19 @@ export function truncatableReducer(state = initialState, action: TruncatableActi
|
|||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case TruncatableActionTypes.TOGGLE: {
|
case TruncatableActionTypes.COLLAPSE: {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
[action.id]: {
|
||||||
|
collapsed: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} case TruncatableActionTypes.EXPAND: {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
[action.id]: {
|
||||||
|
collapsed: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} case TruncatableActionTypes.TOGGLE: {
|
||||||
if (!state[action.id]) {
|
if (!state[action.id]) {
|
||||||
state[action.id] = {collapsed: false};
|
state[action.id] = {collapsed: false};
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { createSelector, MemoizedSelector, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { TruncatablesState, TruncatableState } from './truncatable.reducer';
|
import { TruncatablesState, TruncatableState } from './truncatable.reducer';
|
||||||
import { TruncatableToggleAction } from './truncatable.actions';
|
import { TruncatableExpandAction, TruncatableToggleAction, TruncatableCollapseAction } from './truncatable.actions';
|
||||||
import { hasValue } from '../empty.util';
|
import { hasValue } from '../empty.util';
|
||||||
|
|
||||||
const truncatableStateSelector = (state: TruncatablesState) => state.truncatable;
|
const truncatableStateSelector = (state: TruncatablesState) => state.truncatable;
|
||||||
@@ -16,7 +16,6 @@ export class TruncatableService {
|
|||||||
isCollapsed(id: string): Observable<boolean> {
|
isCollapsed(id: string): Observable<boolean> {
|
||||||
return this.store.select(truncatableByIdSelector(id))
|
return this.store.select(truncatableByIdSelector(id))
|
||||||
.map((object: TruncatableState) => {
|
.map((object: TruncatableState) => {
|
||||||
console.log(object);
|
|
||||||
if (object) {
|
if (object) {
|
||||||
return object.collapsed;
|
return object.collapsed;
|
||||||
} else {
|
} else {
|
||||||
@@ -28,6 +27,14 @@ export class TruncatableService {
|
|||||||
public toggle(id: string): void {
|
public toggle(id: string): void {
|
||||||
this.store.dispatch(new TruncatableToggleAction(id));
|
this.store.dispatch(new TruncatableToggleAction(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public collapse(id: string): void {
|
||||||
|
this.store.dispatch(new TruncatableCollapseAction(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public expand(id: string): void {
|
||||||
|
this.store.dispatch(new TruncatableExpandAction(id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function truncatableByIdSelector(id: string): MemoizedSelector<TruncatablesState, TruncatableState> {
|
function truncatableByIdSelector(id: string): MemoizedSelector<TruncatablesState, TruncatableState> {
|
||||||
@@ -36,8 +43,6 @@ function truncatableByIdSelector(id: string): MemoizedSelector<TruncatablesState
|
|||||||
|
|
||||||
export function keySelector<T>(key: string): MemoizedSelector<TruncatablesState, T> {
|
export function keySelector<T>(key: string): MemoizedSelector<TruncatablesState, T> {
|
||||||
return createSelector(truncatableStateSelector, (state: TruncatableState) => {
|
return createSelector(truncatableStateSelector, (state: TruncatableState) => {
|
||||||
console.log(state, 'test');
|
|
||||||
|
|
||||||
if (hasValue(state)) {
|
if (hasValue(state)) {
|
||||||
return state[key];
|
return state[key];
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,4 +1,12 @@
|
|||||||
@function calculateRem($size) {
|
@function calculateRem($size) {
|
||||||
$remSize: $size / 16px;
|
$remSize: $size / 16px;
|
||||||
@return $remSize;
|
@return $remSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@function strip-unit($number) {
|
||||||
|
@if type-of($number) == 'number' and not unitless($number) {
|
||||||
|
@return $number / ($number * 0 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $number;
|
||||||
}
|
}
|
Reference in New Issue
Block a user