WIP PWA for Grain
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add action dialog component

+111
+111
src/components/organisms/grain-action-dialog.js
··· 1 + import { LitElement, html, css } from 'lit'; 2 + 3 + export class GrainActionDialog extends LitElement { 4 + static properties = { 5 + open: { type: Boolean, reflect: true }, 6 + actions: { type: Array } 7 + }; 8 + 9 + static styles = css` 10 + :host { 11 + display: none; 12 + } 13 + :host([open]) { 14 + display: block; 15 + } 16 + .overlay { 17 + position: fixed; 18 + inset: 0; 19 + background: rgba(0, 0, 0, 0.5); 20 + display: flex; 21 + align-items: center; 22 + justify-content: center; 23 + z-index: 1000; 24 + padding: var(--space-md); 25 + } 26 + .dialog { 27 + background: var(--color-bg-primary); 28 + border-radius: 12px; 29 + min-width: 280px; 30 + max-width: 320px; 31 + overflow: hidden; 32 + } 33 + .action-button { 34 + display: block; 35 + width: 100%; 36 + padding: 14px 16px; 37 + background: none; 38 + border: none; 39 + border-bottom: 1px solid var(--color-border); 40 + text-align: center; 41 + cursor: pointer; 42 + font-size: var(--font-size-sm); 43 + font-family: inherit; 44 + color: var(--color-text-primary); 45 + } 46 + .action-button:last-child { 47 + border-bottom: none; 48 + } 49 + .action-button:hover { 50 + background: var(--color-bg-secondary); 51 + } 52 + .action-button.danger { 53 + color: #ff4444; 54 + } 55 + .action-button.cancel { 56 + color: var(--color-text-secondary); 57 + border-top: 1px solid var(--color-border); 58 + margin-top: 8px; 59 + } 60 + `; 61 + 62 + constructor() { 63 + super(); 64 + this.open = false; 65 + this.actions = []; 66 + } 67 + 68 + #handleOverlayClick(e) { 69 + if (e.target.classList.contains('overlay')) { 70 + this.#close(); 71 + } 72 + } 73 + 74 + #handleAction(action) { 75 + this.dispatchEvent(new CustomEvent('action', { 76 + detail: { action }, 77 + bubbles: true, 78 + composed: true 79 + })); 80 + this.#close(); 81 + } 82 + 83 + #close() { 84 + this.dispatchEvent(new CustomEvent('close', { 85 + bubbles: true, 86 + composed: true 87 + })); 88 + } 89 + 90 + render() { 91 + return html` 92 + <div class="overlay" @click=${this.#handleOverlayClick}> 93 + <div class="dialog"> 94 + ${this.actions.map(item => html` 95 + <button 96 + class="action-button ${item.danger ? 'danger' : ''}" 97 + @click=${() => this.#handleAction(item.action)} 98 + > 99 + ${item.label} 100 + </button> 101 + `)} 102 + <button class="action-button cancel" @click=${this.#close}> 103 + Cancel 104 + </button> 105 + </div> 106 + </div> 107 + `; 108 + } 109 + } 110 + 111 + customElements.define('grain-action-dialog', GrainActionDialog);