forked from
grain.social/grain-pwa
WIP PWA for Grain
1import { LitElement, html, css } from 'lit';
2import '../atoms/grain-icon.js';
3
4export class GrainStatCount extends LitElement {
5 static properties = {
6 icon: { type: String },
7 count: { type: Number },
8 filled: { type: Boolean },
9 interactive: { type: Boolean }
10 };
11
12 static styles = css`
13 :host {
14 display: inline-flex;
15 align-items: center;
16 gap: var(--space-xs);
17 color: var(--color-text-primary);
18 }
19 button {
20 display: flex;
21 align-items: center;
22 justify-content: center;
23 background: none;
24 border: none;
25 padding: var(--space-sm);
26 margin: calc(-1 * var(--space-sm));
27 cursor: pointer;
28 color: inherit;
29 border-radius: var(--border-radius);
30 transition: opacity 0.2s;
31 }
32 button:hover {
33 opacity: 0.7;
34 }
35 button:active {
36 transform: scale(0.95);
37 }
38 .count {
39 font-size: var(--font-size-sm);
40 font-weight: var(--font-weight-semibold);
41 }
42 `;
43
44 constructor() {
45 super();
46 this.count = 0;
47 this.filled = false;
48 this.interactive = false;
49 }
50
51 #formatCount(n) {
52 if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`;
53 if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;
54 return n.toString();
55 }
56
57 #handleClick() {
58 if (this.interactive) {
59 this.dispatchEvent(new CustomEvent('stat-click', { bubbles: true, composed: true }));
60 }
61 }
62
63 get #iconName() {
64 if (this.icon === 'heart' && this.filled) {
65 return 'heartFilled';
66 }
67 return this.icon;
68 }
69
70 render() {
71 const color = this.icon === 'heart' && this.filled ? 'var(--color-heart)' : 'inherit';
72
73 return html`
74 <button
75 type="button"
76 aria-label=${this.icon}
77 style="color: ${color}"
78 @click=${this.#handleClick}
79 >
80 <grain-icon name=${this.#iconName} size="16"></grain-icon>
81 </button>
82 ${this.count > 0 ? html`
83 <span class="count">${this.#formatCount(this.count)}</span>
84 ` : ''}
85 `;
86 }
87}
88
89customElements.define('grain-stat-count', GrainStatCount);