import { WebComponent, enable, string, type PropChange } from '../src/index.ts'; import { register } from './main-context.ts'; /** * Closed shadow root built from `template`: `el.shadowRoot` is `null` from the * outside, but the component still reaches its own DOM through `this.root` and * `this.refs`. Closed hides the internals, not the data; `message` is still a * reflected attribute. */ const SPEC = { shadow: 'closed', template: `
`, css: ` :host { display: grid; gap: 0.75rem; padding: 1.25rem; border-radius: 14px; background: repeating-linear-gradient(-45deg, #1f1b2e 0 12px, #241f36 12px 24px); color: #f0eaff; font: 0.95rem/1.5 ui-monospace, 'SF Mono', Menlo, monospace; } button { all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: 0.5rem; justify-self: start; padding: 0.4rem 0.8rem; border-radius: 999px; background: #3b3355; transition: background 150ms ease; } button:hover { background: #4a4170; } button:focus-visible { outline: 2px solid #b69cff; outline-offset: 2px; } .lock { transition: transform 300ms ease; } .message { margin: 0; filter: blur(6px); user-select: none; transition: filter 400ms ease; } :host([revealed]) .message { filter: none; user-select: auto; } :host([revealed]) .lock { transform: rotate(-20deg); } `, element: { toggle: 'button', hint: '.hint', lock: '.lock', message: 'p.message', }, props: { message: string({ default: '' }), revealed: enable(), }, } as const; export class SecretNote extends WebComponent(SPEC) { override onConnect(): void { // Not `events: { click: true }`: a listener on the host can't see into a closed // root (`composedPath()` stops at the host), so listen on the button itself. this.refs.toggle?.addEventListener('click', this, { signal: this.signal }); this.#render(); register(this, { onSecret: (secret) => { this.message = secret; }, }); } override onPropChange(_change: PropChange