Initial commit
This commit is contained in:
114
landing/src/app/ui/member-card.ts
Normal file
114
landing/src/app/ui/member-card.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
||||
import {
|
||||
PiAvatarComponent,
|
||||
PiCardCenterContentDirective,
|
||||
PiCardComponent,
|
||||
PiCardTopContentDirective,
|
||||
PiChipComponent,
|
||||
PiTextColorDirective,
|
||||
PiTextSmallDirective,
|
||||
PiTextViewerLinkComponent,
|
||||
PiTextWeightBoldDirective,
|
||||
} from '@pi/ui';
|
||||
|
||||
import type { Copy } from '../core/copy';
|
||||
import type { PublicMember } from '../core/api.types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-member-card',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
PiCardComponent,
|
||||
PiCardTopContentDirective,
|
||||
PiCardCenterContentDirective,
|
||||
PiAvatarComponent,
|
||||
PiChipComponent,
|
||||
PiTextViewerLinkComponent,
|
||||
PiTextSmallDirective,
|
||||
PiTextColorDirective,
|
||||
PiTextWeightBoldDirective,
|
||||
],
|
||||
template: `
|
||||
<pi-card
|
||||
class="landing-card"
|
||||
[hasShadow]="true"
|
||||
[isStroked]="true"
|
||||
gap="m"
|
||||
padding="l"
|
||||
innerPadding="none"
|
||||
alignment="center"
|
||||
context="public"
|
||||
>
|
||||
<pi-card-top-content>
|
||||
<!-- The avatar falls back to initials on its own, and again to an
|
||||
icon, so a missing photo never leaves a hole. -->
|
||||
<pi-avatar
|
||||
size="x-large"
|
||||
shape="circle"
|
||||
color="primary"
|
||||
icon="user"
|
||||
[image]="member().avatar?.url"
|
||||
[alt]="member().avatar?.alt"
|
||||
[name]="member().name"
|
||||
/>
|
||||
</pi-card-top-content>
|
||||
|
||||
<pi-card-center-content>
|
||||
<h3 bold class="landing-card__title">{{ member().name }}</h3>
|
||||
@if (member().role || member().headline; as subtitle) {
|
||||
<p s pi-color color="grey">{{ subtitle }}</p>
|
||||
}
|
||||
|
||||
<div class="landing-card__chips landing-card__chips--center">
|
||||
@if (member().is_founder) {
|
||||
<pi-chip size="small" color="primary" [label]="copy().labels.founder" />
|
||||
}
|
||||
@if (!member().is_current) {
|
||||
<pi-chip
|
||||
size="small"
|
||||
appearance="outline"
|
||||
[label]="copy().labels.alumni"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (links().length > 0) {
|
||||
<div class="landing-card__links landing-card__links--center">
|
||||
@for (link of links(); track link.label) {
|
||||
<pi-text-viewer-link
|
||||
icon="link"
|
||||
iconSize="x-small"
|
||||
[value]="link.label"
|
||||
[link]="link.url"
|
||||
target="_blank"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</pi-card-center-content>
|
||||
</pi-card>
|
||||
`,
|
||||
})
|
||||
export class MemberCard {
|
||||
readonly member = input.required<PublicMember>();
|
||||
readonly copy = input.required<Copy>();
|
||||
|
||||
protected readonly links = computed(() => {
|
||||
const member = this.member();
|
||||
const entries = Object.entries(member.links ?? {}).map(([label, url]) => ({
|
||||
label,
|
||||
url,
|
||||
}));
|
||||
|
||||
// The username is handed over separately, so build the URL rather than
|
||||
// hope somebody also filled the links map in.
|
||||
if (member.github_username && !entries.some((link) => link.label === 'github')) {
|
||||
entries.unshift({
|
||||
label: 'github',
|
||||
url: `https://github.com/${member.github_username}`,
|
||||
});
|
||||
}
|
||||
|
||||
return entries.slice(0, 3);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user