Skip to content

Commit

Permalink
Initial version of directory
Browse files Browse the repository at this point in the history
  • Loading branch information
akariv committed Dec 11, 2023
1 parent 88728b2 commit ff112d6
Show file tree
Hide file tree
Showing 18 changed files with 440 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@
flex: 1 1 auto;
height: 100%;
border-radius: 2px;
background: @gray5;
background: @color-gray-5;

&.active {
background: @bright-multiply;
Expand Down
12 changes: 12 additions & 0 deletions projects/chronomaps/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ import { Component } from '@angular/core';
import { marked } from 'marked';
import { DataService } from './data.service';

// import * as durationPlugin from 'dayjs/plugin/duration';
import * as relativeTimePlugin from 'dayjs/plugin/relativeTime';
import * as utcPlugin from 'dayjs/plugin/utc';
import * as dayjs from 'dayjs';

// import 'dayjs/locale/he';
// dayjs.locale('he')

// dayjs.extend(durationPlugin);
dayjs.extend(relativeTimePlugin);
dayjs.extend(utcPlugin);

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
Expand Down
58 changes: 46 additions & 12 deletions projects/chronomaps/src/app/data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type Author = {
};


export type ContentItem = {
export class ContentItem {
id: number;
title: string;
post_timestamp: Date;
Expand All @@ -38,8 +38,24 @@ export type ContentItem = {
authors: Author[];
tags: string[];
related: ContentItem[];
lastModified: Date;
};

export class TimelineItem extends ContentItem {
index: number;
next: ContentItem | null;
prev: ContentItem | null;
timestamp: Date;

x: number;
cx: number = 0;
cy: number = 0;

centerTimestamp: Date;
k: number = 0;
clustered: number;
indexes: number[] = [];
}

export class ChronomapDatabase extends BaserowDatabase {

Expand Down Expand Up @@ -68,10 +84,12 @@ export class ChronomapDatabase extends BaserowDatabase {
primaryColor = signal<string>('');
secondaryColor = signal<string>('');

contentItems = signal<ContentItem[]>([]);
timelineItems = signal<TimelineItem[]>([]);

lastModified = signal<Date>(new Date());
allLayers: string[] = [];
allContentItems: ContentItem[];
authors = signal<{[key: string]: Author}>({});

constructor(chronomap: any, private http: HttpClient) {
super(BASEROW_ENDPOINT, chronomap.Database_Token, chronomap.Database_ID);
Expand Down Expand Up @@ -130,18 +148,20 @@ export class ChronomapDatabase extends BaserowDatabase {
const authors: any = {};
authorsTable?.rows.forEach((row: any) => {
authors[row.Name] = {
name: row.Name,
email: row.Email,
status: row.Status,
status: row.Status.value,
};
});
this.authors.set(authors);
const contentItems: ContentItem[] = [];
contentTable?.rows.forEach((row: any) => {
const item: any = {
id: row.id,
title: row.Title,
post_timestamp: dayjs(row.Post_Timestamp).toDate(),
status: row.Status,
type: row.Type,
status: row.Status.value,
type: row.Type.value,
youtube_video_id: row.Youtube_Video_Id,
content: row.Content,
image: row.Image?.[0]?.url,
Expand All @@ -160,6 +180,7 @@ export class ChronomapDatabase extends BaserowDatabase {
authors: row.Authors?.map((x: any) => authors[x.value]) || [],
tags: row.Tags?.map((x: any) => x.value) || [],
related: row.Related?.map((x: any) => ({id: x.id})) || [],
lastModified: dayjs(row.Last_Modified).toDate(),
};
item.alt_post_timestamp = row.Alt_Post_Timestamp ? dayjs(row.Alt_Post_Timestamp).toDate() : item.post_timestamp;
row.Map_Layer.forEach((x: any) => {
Expand All @@ -177,12 +198,12 @@ export class ChronomapDatabase extends BaserowDatabase {
item.off_map_layers.push(layer);
}
});

const contentItem: ContentItem = item;
if (contentItem.status === 'Published') {
if (contentItem.authors.find((author: Author) => author.status === 'Editor' || author.status === 'Contributor')) {
contentItems.push(contentItem);
}
}
if (contentItem.status !== 'Published') { return; }
if (!contentItem.authors.find((author: Author) => author.status === 'Editor' || author.status === 'Contributor')) { return; }
if (!contentItem.post_timestamp && !contentItem.alt_post_timestamp) { return; }
contentItems.push(contentItem);
});
contentItems.forEach((item: ContentItem) => {
item.related = item.related.map((id: ContentItem) => (contentItems.find((i: ContentItem) => i.id === id.id) || {}) as ContentItem).filter((i: ContentItem) => !!i.id);
Expand All @@ -191,7 +212,20 @@ export class ChronomapDatabase extends BaserowDatabase {
}),
).subscribe((contentItems: ContentItem[]) => {
this.allContentItems = contentItems;
this.contentItems.set(contentItems);

const timelineItems: TimelineItem[] = contentItems.map((item: ContentItem, index: number) => {
const ti = new TimelineItem();
Object.assign(ti, item);
ti.timestamp = ti.post_timestamp || ti.alt_post_timestamp;
return ti;
});
timelineItems.forEach((item: TimelineItem, index: number) => {
item.index = index;
item.next = timelineItems[index + 1] || null;
item.prev = timelineItems[index - 1] || null;
});
this.lastModified.set(timelineItems.map(x => x.lastModified).reduce((a, b) => a > b ? a : b, new Date(1970, 1, 1)));
this.timelineItems.set(timelineItems);
});
}
}
Expand Down Expand Up @@ -230,7 +264,7 @@ export class DirectoryDatabase extends BaserowDatabase {
this.description.set(keyValues.Description.value);
this.fullDescription.set(keyValues.Full_Description.value);
this.logos.set((keyValues.Logos?.images || []).map((i: any) => i.url));
this.logoLinks.set(keyValues.Logos?.images?.value?.split(',') || []);
this.logoLinks.set(keyValues.Logos?.value?.split(',') || []);
this.primaryColor.set(keyValues.Primary_Color.value);
this.secondaryColor.set(keyValues.Secondary_Color.value);
this.zoomFrom.set(keyValues.Zoom_From.value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
<div class='meta'>
<img [src]='chronomap.thumbnail()'>
<div class='titles'>
<div class='title' [style.color]='data.directory.secondaryColor()'>{{chronomap.title()}}</div>
<div class='title' [style.color]='data.directory.secondaryColor()'>
<span>{{chronomap.title()}}</span>
<div class='arrow-container' #arrowContainer>
<div class='arrow' [style.right]='arrowRight + "px"'>
<svg xmlns="http://www.w3.org/2000/svg" width="1999" height="9" viewBox="0 0 1999 9" fill="none">
<path d="M1998.38 4.94371C1998.58 4.74845 1998.58 4.43187 1998.38 4.2366L1995.2 1.05462C1995 0.859362 1994.69 0.859362 1994.49 1.05462C1994.3 1.24989 1994.3 1.56647 1994.49 1.76173L1997.32 4.59016L1994.49 7.41858C1994.3 7.61385 1994.3 7.93043 1994.49 8.12569C1994.69 8.32095 1995 8.32095 1995.2 8.12569L1998.38 4.94371ZM0.0273438 5.09033L1998.03 5.09016L1998.03 4.09016L0.0273437 4.09033L0.0273438 5.09033Z" [attr.fill]='data.directory.primaryColor()'/>
</svg>
</div>
<div class='time'>Updated {{lastModified}}</div>
</div>
</div>
<div class='subtitle' [style.color]='data.directory.primaryColor()'>{{chronomap.subtitle()}}</div>
<div class='counts'>13 perspectives todo</div>
<div class='counts'>{{authorsMsg()}}</div>
</div>
<div class='time'>Updated 2 weeks ago</div>
</div>
<app-time-line [id]='chronomap.title()' [state]='timelineState'></app-time-line>
<app-time-line [id]='chronomap.title()' [state]='timelineState' [minDate]='minDate()' [maxDate]='maxDate()'
[chronomap]='chronomap' [showHovers]='false' [hoverable]='true' [hovered]='hovered'></app-time-line>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

:host {
width: 100%;
height: 104px;
height: 124px;
position: relative;

.meta {
Expand All @@ -11,6 +11,7 @@
gap: 16px;
overflow: hidden;
width: 100%;
height: 104px;
padding-left: 40px;
padding-right: 16px;
position: relative;
Expand All @@ -26,14 +27,53 @@
.titles {
display: flex;
flex-flow: column nowrap;
width: 100%;

.title {
display: flex;
flex-flow: row nowrap;
align-items: center;
gap: 8px;
pointer-events: all;
.font-sourcecode;
font-size: 18px;
font-weight: 700;
line-height: normal;
text-transform: uppercase;

span {
.font-sourcecode;
font-size: 18px;
font-weight: 700;
line-height: normal;
text-transform: uppercase;
white-space: nowrap;
}

.arrow-container {
flex: 1 1 100%;
height: 20px;
position: relative;
overflow: hidden;
display: flex;
flex-flow: row;
align-items: center;

.arrow {
position: absolute;
right: 800px;
transition: right 0.5s ease-in;
}

.time {
position: absolute;
right: 4px;
color: @color-gray-3;
.font-manrope;
font-size: 12px;
font-weight: 400;
line-height: normal;
letter-spacing: 0.24px;
transition: opacity 0.5s ease-in;
opacity: 1;
}

}
}

.subtitle {
Expand All @@ -50,19 +90,21 @@
.font-manrope;
font-size: 13px;
font-weight: 400;
line-height: 23px;
line-height: 100%;
letter-spacing: 0.13px;
}
}

.time {
margin-left: auto;
color: @color-gray-3;
.font-manrope;
font-size: 12px;
font-weight: 400;
line-height: normal;
letter-spacing: 0.24px;
}

&:hover {
.meta > .titles > .title > .arrow-container {
.arrow {
right: 0 !important;
}
.time {
opacity: 0;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,77 @@
import { Component, Input, WritableSignal, effect, signal } from '@angular/core';
import { ChronomapDatabase, DataService, DirectoryDatabase } from '../../data.service';
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output, ViewChild, WritableSignal, computed, effect, signal } from '@angular/core';
import { Author, ChronomapDatabase, DataService, DirectoryDatabase } from '../../data.service';
import * as dayjs from 'dayjs';
import { last } from 'rxjs';

@Component({
selector: 'app-directory-item',
templateUrl: './directory-item.component.html',
styleUrl: './directory-item.component.less'
styleUrl: './directory-item.component.less',
host: {
'(mouseenter)': 'hovered.set(true)',
'(mouseleave)': 'hovered.set(false)',
}
})
export class DirectoryItemComponent {
export class DirectoryItemComponent implements AfterViewInit {

@Input() chronomap: ChronomapDatabase;
@Input() timelineState: WritableSignal<string>;
@Input() minDate: WritableSignal<Date>;
@Input() maxDate: WritableSignal<Date>;
@Input() arrowRight: number;

@Output() alignment = new EventEmitter<number>();

@ViewChild('arrowContainer') arrowContainer: ElementRef<HTMLElement>;

hovered = signal<boolean>(false);

authorsMsg = computed(() => {
const timeline = this.chronomap.timelineItems();
const authors: {[key: string]: Author} = {};
const editors: string[] = [];
let contrubutors = 0;
timeline.forEach((item) => {
item.authors?.forEach((author) => {
authors[author.name] = author;
});
});
Object.values(authors).forEach((author) => {
if (author.status === 'Editor') {
editors.push(author.name);
} else if (author.status === 'Contributor') {
contrubutors++;
}
});
if (editors.length > 1) {
const last = editors.pop();
editors[editors.length - 1] += ` and ${last}`;
}
let ret = editors.join(', ');
if (contrubutors > 0) {
if (ret.length > 0) {
ret += ` +${contrubutors} more`;
} else {
ret += `${contrubutors} contributor${contrubutors > 1 ? 's' : ''}`;
}
}
ret = `${timeline.length} perspectives by ${ret}`;
return ret;
});

constructor(public data: DataService) {
effect(() => {
const items = this.chronomap.timelineItems();
console.log('items', this.chronomap.title(), items);
console.log('timelineState', this.timelineState());
});
}

ngAfterViewInit(): void {
this.alignment.next(this.arrowContainer.nativeElement.getBoundingClientRect().width - 26);
}

get lastModified() {
return dayjs(this.chronomap.lastModified()).fromNow();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
<div class='title'>
<img [src]="data.directory.titleImageUrl()">
<div class='description'>
{{data.directory.titleImageUrl()}}
<div class='name'>{{data.directory.description()}}</div>
<div class='short' [style.color]='data.directory.secondaryColor()'>
{{data.directory.description()}}
<a class='toggle' (click)='fullDescription.set(true)' *ngIf='!fullDescription()' [style.color]='data.directory.primaryColor()'>more</a>
</div>
<div class='long' [style.color]='data.directory.primaryColor()' *ngIf='fullDescription()'>
<span [innerHTML]='marked(data.directory.fullDescription())'></span>
<a class='toggle' (click)='fullDescription.set(false)'>less</a>
</div>
<div class='logos' *ngIf='fullDescription()'>
@for (logo of data.directory.logos(); track $index) {
<a [href]='data.directory.logoLinks()[$index]' target="_blank"><img [src]='logo' [style.height]='"60px"'></a>
}
</div>
</div>
</div>
<div class='chronomaps'>
@for (chronomap of data.directory.chronomaps(); track chronomap.id) {
<app-directory-item [chronomap]="chronomap" [timelineState]='timelineState'></app-directory-item>
<app-directory-item [chronomap]="chronomap" [timelineState]='timelineState'
[minDate]='minDate' [maxDate]='maxDate'
[arrowRight]='arrowRight()' (alignment)='align(chronomap.title(), $event)'></app-directory-item>
}
</div>
Loading

0 comments on commit ff112d6

Please sign in to comment.