183 lines
4.9 KiB
TypeScript
183 lines
4.9 KiB
TypeScript
|
|
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>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
}
|