import { css, html, LitElement, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import type { GetCurrentDashaResponse, GetMajorDashasResponse, GetSubDashasResponse, } from '../types/index.js'; import { baseStyles } from '../utils/base-styles.js'; import { formatNumber } from '../utils/format.js'; type DashaData = | GetCurrentDashaResponse | GetMajorDashasResponse | GetSubDashasResponse; type DashaPeriod = { planet: string; startDate: string; endDate: string; durationYears: number; interpretation?: string; }; /** * Dasha timeline. Renders /vedic-astrology/dasha/{current,major,sub/{...}}. * Default mode shows the active mahadasha + antardasha + pratyantardasha. * Switch to period="major" for the full 120-year Vimshottari timeline. */ @customElement('roxy-dasha-timeline') export class RoxyDashaTimeline extends LitElement { static styles = [ baseStyles, css` .wrap { display: grid; gap: var(--roxy-space-md, 1rem); } .head { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--roxy-space-sm, 0.5rem); } .title { margin: 0; font-size: var(--roxy-text-lg, 1.125rem); font-weight: var(--roxy-weight-bold, 600); } .nakshatra { color: var(--roxy-muted, #71717a); font-size: var(--roxy-text-sm, 0.875rem); } .current { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: var(--roxy-space-md, 1rem); background: var(--roxy-bg, #fff); border: 1px solid var(--roxy-border, #e4e4e7); border-radius: var(--roxy-radius-md, 8px); padding: var(--roxy-space-md, 1rem); box-shadow: var(--roxy-shadow-sm); } .current div span:first-child { display: block; color: var(--roxy-muted, #71717a); font-size: var(--roxy-text-xs, 0.75rem); text-transform: uppercase; letter-spacing: 0.06em; } .current div strong { font-size: var(--roxy-text-base, 1rem); color: var(--roxy-fg, #0a0a0a); } .timeline { display: grid; gap: var(--roxy-space-xs, 0.25rem); } .bar { display: grid; grid-template-columns: 5rem 1fr 8rem; gap: var(--roxy-space-sm, 0.5rem); align-items: center; font-size: var(--roxy-text-sm, 0.875rem); } .bar-track { height: 14px; background: var(--roxy-border, #e4e4e7); border-radius: var(--roxy-radius-full, 9999px); overflow: hidden; } .bar-track > span { display: block; height: 100%; background: var(--roxy-accent, #f59e0b); transition: width var(--roxy-motion-duration, 200ms) var(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)); } .dates { color: var(--roxy-muted, #71717a); font-size: var(--roxy-text-xs, 0.75rem); font-variant-numeric: tabular-nums; text-align: right; } `, ]; @property({ attribute: false }) data: DashaData | null = null; @property({ type: String, reflect: true }) period: 'current' | 'major' | 'sub' = 'current'; render() { const d = this.data; if (!d) return html`
No dasha data
`; const periods = this.collectPeriods(d); const maxYears = periods.length ? Math.max(...periods.map((p) => p.durationYears)) : 0; return html`

${ this.period === 'major' ? 'Vimshottari Mahadasha' : this.period === 'sub' ? 'Antardasha' : 'Active dashas' }

${ 'nakshatraName' in d && d.nakshatraName ? html`
Moon nakshatra: ${d.nakshatraName} ${'nakshatraLord' in d && d.nakshatraLord ? html`(lord ${d.nakshatraLord})` : nothing}
` : nothing }
${this.period === 'current' ? this.renderCurrent(d) : nothing} ${ periods.length > 0 ? html`
${periods.map((p) => this.renderBar(p, maxYears))}
` : nothing }
`; } private renderCurrent(d: DashaData) { if (!('mahadasha' in d)) return nothing; return html`
${ 'mahadasha' in d && d.mahadasha ? html`
Mahadasha ${d.mahadasha.planet} ${ 'remainingInMahadasha' in d && d.remainingInMahadasha ? html`${formatNumber(d.remainingInMahadasha.years + d.remainingInMahadasha.months / 12, 1)} years left` : nothing }
` : nothing } ${ 'antardasha' in d && d.antardasha ? html`
Antardasha ${d.antardasha.planet} ${ 'remainingInAntardasha' in d && d.remainingInAntardasha ? html`${formatNumber(d.remainingInAntardasha.years + d.remainingInAntardasha.months / 12, 1)} years left` : nothing }
` : nothing } ${ 'pratyantardasha' in d && d.pratyantardasha ? html`
Pratyantardasha ${d.pratyantardasha.planet} ${ 'remainingInPratyantardasha' in d && d.remainingInPratyantardasha ? html`${formatNumber(d.remainingInPratyantardasha.years + d.remainingInPratyantardasha.months / 12, 1)} years left` : nothing }
` : nothing }
`; } private collectPeriods(d: DashaData): DashaPeriod[] { if ('mahadashas' in d && d.mahadashas?.length) return d.mahadashas; if ('antardashas' in d && d.antardashas?.length) return d.antardashas; return []; } private renderBar(p: DashaPeriod, max: number) { const years = p.durationYears; const width = max > 0 ? (years / max) * 100 : 0; return html`
${p.planet} ${p.startDate ? formatYear(p.startDate) : ''} ${p.endDate ? html`- ${formatYear(p.endDate)}` : ''}
`; } } function formatYear(s: string): string { const m = s.match(/^(\d{4})/); return m ? m[1] : s; } declare global { interface HTMLElementTagNameMap { 'roxy-dasha-timeline': RoxyDashaTimeline; } }