Files
catalog/ts_web/elements/idp-approval-card.ts
T

183 lines
4.9 KiB
TypeScript
Raw Normal View History

import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
import { idpElementStyles } from './tokens.js';
import './idp-badge.js';
import './idp-button.js';
import './idp-icon.js';
declare global {
interface HTMLElementTagNameMap {
'idp-approval-card': IdpApprovalCard;
}
}
@customElement('idp-approval-card')
export class IdpApprovalCard extends DeesElement {
public static demo = () => html`
<idp-approval-card
app-name="GitHub"
app-initials="GH"
app-color="#24292F"
request-text="Sign in to github.com"
location="Berlin · DE"
device="Safari · MacBook Pro"
risk="trusted"
time-label="now"
></idp-approval-card>
`;
public static demoGroups = ['idp.global v3 approval surfaces'];
@property({ type: String, attribute: 'app-name' })
public accessor appName = 'GitHub';
@property({ type: String, attribute: 'app-initials' })
public accessor appInitials = 'GH';
@property({ type: String, attribute: 'app-color' })
public accessor appColor = '#24292F';
@property({ type: String, attribute: 'request-text' })
public accessor requestText = 'Sign in to github.com';
@property({ type: String })
public accessor location = 'Berlin · DE';
@property({ type: String })
public accessor device = 'Safari · MacBook Pro';
@property({ type: String })
public accessor risk: 'trusted' | 'warning' | 'low' = 'trusted';
@property({ type: String, attribute: 'time-label' })
public accessor timeLabel = 'now';
@property({ type: Boolean, reflect: true })
public accessor primary = false;
public static styles = [
...idpElementStyles,
css`
:host {
display: block;
}
.card {
background: var(--idp-card);
border: 1px solid var(--idp-border);
border-radius: 12px;
padding: 14px;
color: var(--idp-fg);
}
:host([primary]) .card {
border-color: var(--idp-accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--idp-accent), transparent 92%);
}
.top {
display: flex;
gap: 12px;
align-items: flex-start;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 8px;
background: var(--app-color);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 750;
letter-spacing: -0.03em;
flex: 0 0 auto;
}
.body {
flex: 1;
min-width: 0;
}
.line {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.app {
font-size: 14px;
font-weight: 650;
letter-spacing: -0.02em;
}
.time, .sub, .meta {
color: var(--idp-muted-fg);
}
.time {
font-size: 12px;
}
.sub {
margin-top: 1px;
font-size: 13px;
}
.meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
margin-top: 9px;
font-size: 12px;
}
.meta-item {
display: inline-flex;
align-items: center;
gap: 4px;
}
.actions {
display: flex;
gap: 8px;
margin-top: 12px;
}
idp-button:first-child {
flex: 1;
}
idp-button:last-child {
flex: 2;
}
`,
];
private dispatchAction(actionArg: 'approve' | 'deny') {
this.dispatchEvent(new CustomEvent(`idp-${actionArg}`, {
detail: {
appName: this.appName,
requestText: this.requestText,
},
bubbles: true,
composed: true,
}));
}
public render(): TemplateResult {
const badgeVariant = this.risk === 'warning' ? 'warn' : 'ok';
const badgeText = this.risk === 'warning' ? 'new network' : 'trusted';
return html`
<article class="card" style="--app-color: ${this.appColor}">
<div class="top">
<div class="avatar">${this.appInitials}</div>
<div class="body">
<div class="line">
<div class="app">${this.appName}</div>
<div class="time">${this.timeLabel}</div>
</div>
<div class="sub">${this.requestText}</div>
<div class="meta">
<span class="meta-item"><idp-icon name="location" size="12"></idp-icon>${this.location}</span>
<span class="meta-item"><idp-icon name="laptop" size="12"></idp-icon>${this.device}</span>
<idp-badge variant=${badgeVariant as any}>${badgeText}</idp-badge>
</div>
</div>
</div>
<div class="actions">
<idp-button variant="outline" @click=${() => this.dispatchAction('deny')}>Deny</idp-button>
<idp-button variant="accent" icon="check" @click=${() => this.dispatchAction('approve')}>Approve</idp-button>
</div>
</article>
`;
}
}