import { css, html, nothing } from 'lit'; import { customElement } from 'lit/decorators.js'; import { PLANET_GLYPH } from '../tokens/index.js'; import type { GetHoraResponse } from '../types/index.js'; import { RoxyDataElement } from '../utils/base-element.js'; import { baseStyles } from '../utils/base-styles.js'; import { capitalize } from '../utils/string.js'; /** HH:MM from a tz-naive ISO local datetime, without reparsing (the times are already wall-clock in the requested timezone). */ function clockTime(iso: string | undefined): string { if (!iso) return ''; const t = String(iso).slice(11, 16); return t || String(iso); } type HoraPeriod = NonNullable[number]; /** * Vedic Hora (planetary hours) table. Renders /vedic-astrology/panchang/hora: the 12 daytime and 12 nighttime horas, each ruled by one of the seven classical planets in the Chaldean order, used for electional timing (start a venture in the hora of the planet that favours it). Day horas run sunrise to sunset, night horas sunset to next sunrise; each tile shows the ruling planet and its wall-clock window. */ @customElement('roxy-hora-table') export class RoxyHoraTable extends RoxyDataElement { static styles = [ baseStyles, css` .wrap { background: var(--roxy-surface, #fff); color: var(--roxy-fg, #0a0a0a); border: 1px solid var(--roxy-border, #e4e4e7); border-radius: var(--roxy-radius-md, 8px); padding: var(--roxy-space-lg, 1.5rem); box-shadow: var(--roxy-shadow-sm); display: grid; gap: var(--roxy-space-md, 1rem); } .head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--roxy-space-md, 1rem); flex-wrap: wrap; } .title { font-size: var(--roxy-text-lg, 1.125rem); font-weight: var(--roxy-weight-bold, 600); margin: 0; } .subtitle { color: var(--roxy-muted, #71717a); font-size: var(--roxy-text-sm, 0.875rem); margin: 0; } .cols { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--roxy-space-md, 1rem); } .section-label { font-size: var(--roxy-text-xs, 0.75rem); color: var(--roxy-muted, #71717a); text-transform: uppercase; letter-spacing: 0.06em; font-weight: var(--roxy-weight-bold, 600); margin: 0 0 var(--roxy-space-xs, 0.25rem) 0; } .row { display: flex; align-items: center; gap: var(--roxy-space-sm, 0.5rem); padding: var(--roxy-space-xs, 0.25rem) 0; border-bottom: 1px solid var(--roxy-border, #e4e4e7); font-size: var(--roxy-text-sm, 0.875rem); } .row:last-child { border-bottom: none; } .glyph { font-size: 1.15em; line-height: 1; color: var(--roxy-accent-ink, #b45309); width: 1.25em; text-align: center; } .planet { flex: 1; font-weight: 500; } .time { color: var(--roxy-muted, #71717a); font-variant-numeric: tabular-nums; white-space: nowrap; } .num { color: var(--roxy-muted, #71717a); font-size: var(--roxy-text-xs, 0.75rem); font-variant-numeric: tabular-nums; width: 1.5em; text-align: right; } @container (max-width: 30rem) { .cols { grid-template-columns: 1fr; } } `, ]; protected renderEmpty() { return html`
No hora data
`; } protected renderData(d: GetHoraResponse) { const day = d.dayHoras ?? []; const night = d.nightHoras ?? []; if (day.length === 0 && night.length === 0) return this.renderEmpty(); return html`

Hora

${d.date ? html`

${d.date}

` : nothing}
${this.renderColumn('Day', day)} ${this.renderColumn('Night', night)}
`; } private renderColumn(label: string, horas: HoraPeriod[]) { if (horas.length === 0) return nothing; return html`
${horas.map( (h) => html`
${h.planet} ${clockTime(h.start)}-${clockTime(h.end)} ${typeof h.number === 'number' ? html`${h.number}` : nothing}
`, )}
`; } } declare global { interface HTMLElementTagNameMap { 'roxy-hora-table': RoxyHoraTable; } }