@roxyapi/ui 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/AGENTS.md +169 -0
  2. package/THEMING.md +129 -0
  3. package/dist/cdn/components/biorhythm-chart.js +261 -0
  4. package/dist/cdn/components/biorhythm-chart.js.map +7 -0
  5. package/dist/cdn/components/compatibility-card.js +257 -0
  6. package/dist/cdn/components/compatibility-card.js.map +7 -0
  7. package/dist/cdn/components/dasha-timeline.js +244 -0
  8. package/dist/cdn/components/dasha-timeline.js.map +7 -0
  9. package/dist/cdn/components/data.js +258 -0
  10. package/dist/cdn/components/data.js.map +7 -0
  11. package/dist/cdn/components/dosha-card.js +254 -0
  12. package/dist/cdn/components/dosha-card.js.map +7 -0
  13. package/dist/cdn/components/endpoint-form.js +253 -0
  14. package/dist/cdn/components/endpoint-form.js.map +7 -0
  15. package/dist/cdn/components/guna-milan.js +256 -0
  16. package/dist/cdn/components/guna-milan.js.map +7 -0
  17. package/dist/cdn/components/hexagram.js +275 -0
  18. package/dist/cdn/components/hexagram.js.map +7 -0
  19. package/dist/cdn/components/horoscope-card.js +302 -0
  20. package/dist/cdn/components/horoscope-card.js.map +7 -0
  21. package/dist/cdn/components/kp-planets-table.js +224 -0
  22. package/dist/cdn/components/kp-planets-table.js.map +7 -0
  23. package/dist/cdn/components/location-search.js +267 -0
  24. package/dist/cdn/components/location-search.js.map +7 -0
  25. package/dist/cdn/components/moon-phase.js +251 -0
  26. package/dist/cdn/components/moon-phase.js.map +7 -0
  27. package/dist/cdn/components/natal-chart.js +237 -0
  28. package/dist/cdn/components/natal-chart.js.map +7 -0
  29. package/dist/cdn/components/numerology-card.js +252 -0
  30. package/dist/cdn/components/numerology-card.js.map +7 -0
  31. package/dist/cdn/components/panchang-table.js +234 -0
  32. package/dist/cdn/components/panchang-table.js.map +7 -0
  33. package/dist/cdn/components/synastry-chart.js +303 -0
  34. package/dist/cdn/components/synastry-chart.js.map +7 -0
  35. package/dist/cdn/components/tarot-card.js +260 -0
  36. package/dist/cdn/components/tarot-card.js.map +7 -0
  37. package/dist/cdn/components/tarot-spread.js +261 -0
  38. package/dist/cdn/components/tarot-spread.js.map +7 -0
  39. package/dist/cdn/components/vedic-kundli.js +189 -0
  40. package/dist/cdn/components/vedic-kundli.js.map +7 -0
  41. package/dist/cdn/roxy-ui.js +2552 -0
  42. package/dist/cdn/roxy-ui.js.map +7 -0
  43. package/dist/cdn/widgets.js +114 -0
  44. package/dist/components/biorhythm-chart.d.ts +66 -0
  45. package/dist/components/biorhythm-chart.d.ts.map +1 -0
  46. package/dist/components/biorhythm-chart.js +318 -0
  47. package/dist/components/biorhythm-chart.js.map +7 -0
  48. package/dist/components/compatibility-card.d.ts +46 -0
  49. package/dist/components/compatibility-card.d.ts.map +1 -0
  50. package/dist/components/compatibility-card.js +279 -0
  51. package/dist/components/compatibility-card.js.map +7 -0
  52. package/dist/components/dasha-timeline.d.ts +53 -0
  53. package/dist/components/dasha-timeline.d.ts.map +1 -0
  54. package/dist/components/dasha-timeline.js +269 -0
  55. package/dist/components/dasha-timeline.js.map +7 -0
  56. package/dist/components/data.d.ts +40 -0
  57. package/dist/components/data.d.ts.map +1 -0
  58. package/dist/components/data.js +339 -0
  59. package/dist/components/data.js.map +7 -0
  60. package/dist/components/dosha-card.d.ts +35 -0
  61. package/dist/components/dosha-card.d.ts.map +1 -0
  62. package/dist/components/dosha-card.js +278 -0
  63. package/dist/components/dosha-card.js.map +7 -0
  64. package/dist/components/endpoint-form.d.ts +39 -0
  65. package/dist/components/endpoint-form.d.ts.map +1 -0
  66. package/dist/components/endpoint-form.js +432 -0
  67. package/dist/components/endpoint-form.js.map +7 -0
  68. package/dist/components/guna-milan.d.ts +35 -0
  69. package/dist/components/guna-milan.d.ts.map +1 -0
  70. package/dist/components/guna-milan.js +302 -0
  71. package/dist/components/guna-milan.js.map +7 -0
  72. package/dist/components/hexagram.d.ts +47 -0
  73. package/dist/components/hexagram.d.ts.map +1 -0
  74. package/dist/components/hexagram.js +334 -0
  75. package/dist/components/hexagram.js.map +7 -0
  76. package/dist/components/horoscope-card.d.ts +38 -0
  77. package/dist/components/horoscope-card.d.ts.map +1 -0
  78. package/dist/components/horoscope-card.js +332 -0
  79. package/dist/components/horoscope-card.js.map +7 -0
  80. package/dist/components/kp-planets-table.d.ts +36 -0
  81. package/dist/components/kp-planets-table.d.ts.map +1 -0
  82. package/dist/components/kp-planets-table.js +227 -0
  83. package/dist/components/kp-planets-table.js.map +7 -0
  84. package/dist/components/location-search.d.ts +56 -0
  85. package/dist/components/location-search.d.ts.map +1 -0
  86. package/dist/components/location-search.js +401 -0
  87. package/dist/components/location-search.js.map +7 -0
  88. package/dist/components/moon-phase.d.ts +38 -0
  89. package/dist/components/moon-phase.d.ts.map +1 -0
  90. package/dist/components/moon-phase.js +284 -0
  91. package/dist/components/moon-phase.js.map +7 -0
  92. package/dist/components/natal-chart.d.ts +65 -0
  93. package/dist/components/natal-chart.d.ts.map +1 -0
  94. package/dist/components/natal-chart.js +407 -0
  95. package/dist/components/natal-chart.js.map +7 -0
  96. package/dist/components/numerology-card.d.ts +55 -0
  97. package/dist/components/numerology-card.d.ts.map +1 -0
  98. package/dist/components/numerology-card.js +274 -0
  99. package/dist/components/numerology-card.js.map +7 -0
  100. package/dist/components/panchang-table.d.ts +77 -0
  101. package/dist/components/panchang-table.d.ts.map +1 -0
  102. package/dist/components/panchang-table.js +285 -0
  103. package/dist/components/panchang-table.js.map +7 -0
  104. package/dist/components/synastry-chart.d.ts +52 -0
  105. package/dist/components/synastry-chart.d.ts.map +1 -0
  106. package/dist/components/synastry-chart.js +415 -0
  107. package/dist/components/synastry-chart.js.map +7 -0
  108. package/dist/components/tarot-card.d.ts +47 -0
  109. package/dist/components/tarot-card.d.ts.map +1 -0
  110. package/dist/components/tarot-card.js +281 -0
  111. package/dist/components/tarot-card.js.map +7 -0
  112. package/dist/components/tarot-spread.d.ts +42 -0
  113. package/dist/components/tarot-spread.d.ts.map +1 -0
  114. package/dist/components/tarot-spread.js +271 -0
  115. package/dist/components/tarot-spread.js.map +7 -0
  116. package/dist/components/vedic-kundli.d.ts +45 -0
  117. package/dist/components/vedic-kundli.d.ts.map +1 -0
  118. package/dist/components/vedic-kundli.js +325 -0
  119. package/dist/components/vedic-kundli.js.map +7 -0
  120. package/dist/index.cjs +4174 -0
  121. package/dist/index.cjs.map +7 -0
  122. package/dist/index.d.ts +30 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +4154 -0
  125. package/dist/index.js.map +7 -0
  126. package/dist/manifest.json +24 -0
  127. package/dist/styles/tokens.css +147 -0
  128. package/dist/tokens/index.d.ts +17 -0
  129. package/dist/tokens/index.d.ts.map +1 -0
  130. package/dist/utils/base-styles.d.ts +6 -0
  131. package/dist/utils/base-styles.d.ts.map +1 -0
  132. package/dist/utils/debounce.d.ts +5 -0
  133. package/dist/utils/debounce.d.ts.map +1 -0
  134. package/dist/utils/degree.d.ts +29 -0
  135. package/dist/utils/degree.d.ts.map +1 -0
  136. package/dist/utils/motion.d.ts +13 -0
  137. package/dist/utils/motion.d.ts.map +1 -0
  138. package/package.json +69 -3
  139. package/src/components/biorhythm-chart.ts +290 -0
  140. package/src/components/compatibility-card.ts +231 -0
  141. package/src/components/dasha-timeline.ts +251 -0
  142. package/src/components/data.ts +287 -0
  143. package/src/components/dosha-card.ts +215 -0
  144. package/src/components/endpoint-form.ts +433 -0
  145. package/src/components/guna-milan.ts +245 -0
  146. package/src/components/hexagram.ts +279 -0
  147. package/src/components/horoscope-card.ts +291 -0
  148. package/src/components/kp-planets-table.ts +156 -0
  149. package/src/components/location-search.ts +335 -0
  150. package/src/components/moon-phase.ts +221 -0
  151. package/src/components/natal-chart.ts +298 -0
  152. package/src/components/numerology-card.ts +243 -0
  153. package/src/components/panchang-table.ts +265 -0
  154. package/src/components/synastry-chart.ts +341 -0
  155. package/src/components/tarot-card.ts +235 -0
  156. package/src/components/tarot-spread.ts +224 -0
  157. package/src/components/vedic-kundli.ts +257 -0
  158. package/src/index.ts +61 -0
  159. package/src/styles/tokens.css +147 -0
  160. package/src/tokens/index.ts +130 -0
  161. package/src/types/index.ts +3 -0
  162. package/src/types/types.gen.ts +28526 -0
  163. package/src/utils/base-styles.ts +89 -0
  164. package/src/utils/debounce.ts +13 -0
  165. package/src/utils/degree.ts +64 -0
  166. package/src/utils/motion.ts +18 -0
@@ -0,0 +1,231 @@
1
+ import { css, html, LitElement, nothing } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import { baseStyles } from '../utils/base-styles.js';
4
+
5
+ interface CompatibilityData {
6
+ overallScore?: number;
7
+ score?: number;
8
+ rating?: string;
9
+ relationshipArchetype?: string;
10
+ advice?: string;
11
+ summary?: string;
12
+ categoryScores?: Record<string, number>;
13
+ categoryBreakdown?: Record<string, number>;
14
+ emotional?: number;
15
+ communication?: number;
16
+ romance?: number;
17
+ strengths?: string[];
18
+ challenges?: string[];
19
+ keyAspects?: string[];
20
+ elementBalance?: Record<string, number>;
21
+ person1?: { name?: string; sign?: string; lifePath?: number };
22
+ person2?: { name?: string; sign?: string; lifePath?: number };
23
+ }
24
+
25
+ /**
26
+ * Cross-domain compatibility card. Renders /astrology/compatibility-score,
27
+ * /numerology/compatibility, or /biorhythm/compatibility responses.
28
+ */
29
+ @customElement('roxy-compatibility-card')
30
+ export class RoxyCompatibilityCard extends LitElement {
31
+ static styles = [
32
+ baseStyles,
33
+ css`
34
+ .card {
35
+ background: var(--roxy-bg, #fff);
36
+ border: 1px solid var(--roxy-border, #e4e4e7);
37
+ border-radius: var(--roxy-radius-md, 8px);
38
+ padding: var(--roxy-space-lg, 1.5rem);
39
+ box-shadow: var(--roxy-shadow-sm);
40
+ display: grid;
41
+ gap: var(--roxy-space-md, 1rem);
42
+ }
43
+
44
+ .head {
45
+ display: grid;
46
+ grid-template-columns: 1fr auto;
47
+ align-items: center;
48
+ gap: var(--roxy-space-md, 1rem);
49
+ }
50
+ .head h2 {
51
+ margin: 0;
52
+ font-size: var(--roxy-text-lg, 1.125rem);
53
+ font-weight: var(--roxy-weight-bold, 600);
54
+ text-transform: capitalize;
55
+ }
56
+
57
+ .score {
58
+ font-variant-numeric: tabular-nums;
59
+ font-size: 2rem;
60
+ font-weight: var(--roxy-weight-bold, 600);
61
+ color: var(--roxy-accent-fg, #b45309);
62
+ line-height: 1;
63
+ }
64
+ .rating {
65
+ color: var(--roxy-secondary, #475569);
66
+ font-size: var(--roxy-text-sm, 0.875rem);
67
+ }
68
+
69
+ .bar-row {
70
+ display: grid;
71
+ grid-template-columns: 8rem 1fr 3.5rem;
72
+ gap: var(--roxy-space-sm, 0.5rem);
73
+ align-items: center;
74
+ font-size: var(--roxy-text-sm, 0.875rem);
75
+ }
76
+ .bar {
77
+ height: 8px;
78
+ background: var(--roxy-border, #e4e4e7);
79
+ border-radius: var(--roxy-radius-full, 9999px);
80
+ overflow: hidden;
81
+ }
82
+ .bar > span {
83
+ display: block;
84
+ height: 100%;
85
+ background: var(--roxy-accent, #f59e0b);
86
+ transition:
87
+ width var(--roxy-motion-duration, 200ms)
88
+ var(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));
89
+ }
90
+ .bar-row > span:last-child {
91
+ font-variant-numeric: tabular-nums;
92
+ color: var(--roxy-muted, #71717a);
93
+ text-align: right;
94
+ }
95
+
96
+ .archetype {
97
+ color: var(--roxy-info, #0284c7);
98
+ font-weight: var(--roxy-weight-bold, 600);
99
+ }
100
+
101
+ .lists {
102
+ display: grid;
103
+ grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
104
+ gap: var(--roxy-space-md, 1rem);
105
+ }
106
+ .lists h3 {
107
+ margin: 0 0 var(--roxy-space-xs, 0.25rem);
108
+ font-size: var(--roxy-text-xs, 0.75rem);
109
+ color: var(--roxy-muted, #71717a);
110
+ text-transform: uppercase;
111
+ letter-spacing: 0.06em;
112
+ }
113
+ .lists ul {
114
+ margin: 0;
115
+ padding-left: var(--roxy-space-md, 1rem);
116
+ }
117
+ `,
118
+ ];
119
+
120
+ @property({ attribute: false })
121
+ data: CompatibilityData | null = null;
122
+
123
+ @property({ type: String, reflect: true })
124
+ mode: 'astrology' | 'numerology' | 'biorhythm' = 'astrology';
125
+
126
+ private getBreakdown(): Record<string, number> {
127
+ const d = this.data;
128
+ if (!d) return {};
129
+ if (d.categoryScores) return d.categoryScores;
130
+ if (d.categoryBreakdown) return d.categoryBreakdown;
131
+ const inferred: Record<string, number> = {};
132
+ if (typeof d.emotional === 'number') inferred.emotional = d.emotional;
133
+ if (typeof d.communication === 'number')
134
+ inferred.communication = d.communication;
135
+ if (typeof d.romance === 'number') inferred.romance = d.romance;
136
+ if (d.elementBalance) Object.assign(inferred, d.elementBalance);
137
+ return inferred;
138
+ }
139
+
140
+ render() {
141
+ const d = this.data;
142
+ if (!d)
143
+ return html`<div class="roxy-empty" role="status">No compatibility data</div>`;
144
+ const score = d.overallScore ?? d.score;
145
+ const breakdown = this.getBreakdown();
146
+
147
+ return html`<article
148
+ class="card"
149
+ aria-label=${`Compatibility (${this.mode})`}
150
+ >
151
+ <div class="head">
152
+ <h2>${this.mode} compatibility</h2>
153
+ <div>
154
+ ${
155
+ typeof score === 'number'
156
+ ? html`<div class="score">${score}</div>`
157
+ : nothing
158
+ }
159
+ ${d.rating ? html`<div class="rating">${d.rating}</div>` : nothing}
160
+ </div>
161
+ </div>
162
+
163
+ ${
164
+ Object.keys(breakdown).length > 0
165
+ ? html`<div role="list">
166
+ ${Object.entries(breakdown).map(
167
+ ([k, v]) => html`<div class="bar-row" role="listitem">
168
+ <span style="text-transform: capitalize">${k}</span>
169
+ <span class="bar"
170
+ ><span style="width: ${Math.max(0, Math.min(100, v))}%"></span
171
+ ></span>
172
+ <span>${v}</span>
173
+ </div>`,
174
+ )}
175
+ </div>`
176
+ : nothing
177
+ }
178
+ ${
179
+ d.relationshipArchetype
180
+ ? html`<p>
181
+ <span class="archetype">${d.relationshipArchetype}</span>
182
+ </p>`
183
+ : nothing
184
+ }
185
+ ${d.summary ? html`<p>${d.summary}</p>` : nothing}
186
+ ${d.advice ? html`<p>${d.advice}</p>` : nothing}
187
+ ${
188
+ (d.strengths?.length ?? 0) > 0 || (d.challenges?.length ?? 0) > 0
189
+ ? html`<div class="lists">
190
+ ${
191
+ d.strengths?.length
192
+ ? html`<div>
193
+ <h3>Strengths</h3>
194
+ <ul>
195
+ ${d.strengths.map((s) => html`<li>${s}</li>`)}
196
+ </ul>
197
+ </div>`
198
+ : nothing
199
+ }
200
+ ${
201
+ d.challenges?.length
202
+ ? html`<div>
203
+ <h3>Challenges</h3>
204
+ <ul>
205
+ ${d.challenges.map((s) => html`<li>${s}</li>`)}
206
+ </ul>
207
+ </div>`
208
+ : nothing
209
+ }
210
+ ${
211
+ d.keyAspects?.length
212
+ ? html`<div>
213
+ <h3>Key aspects</h3>
214
+ <ul>
215
+ ${d.keyAspects.map((s) => html`<li>${s}</li>`)}
216
+ </ul>
217
+ </div>`
218
+ : nothing
219
+ }
220
+ </div>`
221
+ : nothing
222
+ }
223
+ </article>`;
224
+ }
225
+ }
226
+
227
+ declare global {
228
+ interface HTMLElementTagNameMap {
229
+ 'roxy-compatibility-card': RoxyCompatibilityCard;
230
+ }
231
+ }
@@ -0,0 +1,251 @@
1
+ import { css, html, LitElement, nothing } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import { baseStyles } from '../utils/base-styles.js';
4
+
5
+ interface DashaPeriod {
6
+ mahadashaLord?: string;
7
+ antardashaLord?: string;
8
+ pratyantardashaLord?: string;
9
+ lord?: string;
10
+ planet?: string;
11
+ startDate?: string;
12
+ endDate?: string;
13
+ years?: number;
14
+ durationYears?: number;
15
+ }
16
+
17
+ interface DashaData {
18
+ moonNakshatra?: string;
19
+ nakshatraName?: string;
20
+ nakshatraLord?: string;
21
+ mahadasha?: DashaPeriod;
22
+ antardasha?: DashaPeriod;
23
+ pratyantardasha?: DashaPeriod;
24
+ mahadashas?: DashaPeriod[];
25
+ antardashas?: DashaPeriod[];
26
+ mahadashaLord?: string;
27
+ mahadashaPeriod?: DashaPeriod;
28
+ birthDashaBalance?: { lord?: string; years?: number };
29
+ totalYears?: number;
30
+ remainingInMahadasha?: number;
31
+ remainingInAntardasha?: number;
32
+ remainingInPratyantardasha?: number;
33
+ }
34
+
35
+ /**
36
+ * Dasha timeline. Renders /vedic-astrology/dasha/{current,major,sub/{...}}.
37
+ * Default mode shows the active mahadasha + antardasha + pratyantardasha.
38
+ * Switch to period="major" for the full 120-year Vimshottari timeline.
39
+ */
40
+ @customElement('roxy-dasha-timeline')
41
+ export class RoxyDashaTimeline extends LitElement {
42
+ static styles = [
43
+ baseStyles,
44
+ css`
45
+ .wrap {
46
+ display: grid;
47
+ gap: var(--roxy-space-md, 1rem);
48
+ }
49
+ .head {
50
+ display: flex;
51
+ justify-content: space-between;
52
+ align-items: center;
53
+ flex-wrap: wrap;
54
+ gap: var(--roxy-space-sm, 0.5rem);
55
+ }
56
+ .title {
57
+ margin: 0;
58
+ font-size: var(--roxy-text-lg, 1.125rem);
59
+ font-weight: var(--roxy-weight-bold, 600);
60
+ }
61
+ .nakshatra {
62
+ color: var(--roxy-muted, #71717a);
63
+ font-size: var(--roxy-text-sm, 0.875rem);
64
+ }
65
+
66
+ .current {
67
+ display: grid;
68
+ grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
69
+ gap: var(--roxy-space-md, 1rem);
70
+ background: var(--roxy-bg, #fff);
71
+ border: 1px solid var(--roxy-border, #e4e4e7);
72
+ border-radius: var(--roxy-radius-md, 8px);
73
+ padding: var(--roxy-space-md, 1rem);
74
+ box-shadow: var(--roxy-shadow-sm);
75
+ }
76
+ .current div span:first-child {
77
+ display: block;
78
+ color: var(--roxy-muted, #71717a);
79
+ font-size: var(--roxy-text-xs, 0.75rem);
80
+ text-transform: uppercase;
81
+ letter-spacing: 0.06em;
82
+ }
83
+ .current div strong {
84
+ font-size: var(--roxy-text-base, 1rem);
85
+ color: var(--roxy-fg, #0a0a0a);
86
+ }
87
+
88
+ .timeline {
89
+ display: grid;
90
+ gap: var(--roxy-space-xs, 0.25rem);
91
+ }
92
+ .bar {
93
+ display: grid;
94
+ grid-template-columns: 5rem 1fr 8rem;
95
+ gap: var(--roxy-space-sm, 0.5rem);
96
+ align-items: center;
97
+ font-size: var(--roxy-text-sm, 0.875rem);
98
+ }
99
+ .bar-track {
100
+ height: 14px;
101
+ background: var(--roxy-border, #e4e4e7);
102
+ border-radius: var(--roxy-radius-full, 9999px);
103
+ overflow: hidden;
104
+ }
105
+ .bar-track > span {
106
+ display: block;
107
+ height: 100%;
108
+ background: var(--roxy-accent, #f59e0b);
109
+ transition:
110
+ width var(--roxy-motion-duration, 200ms)
111
+ var(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));
112
+ }
113
+ .dates {
114
+ color: var(--roxy-muted, #71717a);
115
+ font-size: var(--roxy-text-xs, 0.75rem);
116
+ font-variant-numeric: tabular-nums;
117
+ text-align: right;
118
+ }
119
+ `,
120
+ ];
121
+
122
+ @property({ attribute: false })
123
+ data: DashaData | null = null;
124
+
125
+ @property({ type: String, reflect: true })
126
+ period: 'current' | 'major' | 'sub' = 'current';
127
+
128
+ render() {
129
+ const d = this.data;
130
+ if (!d)
131
+ return html`<div class="roxy-empty" role="status">No dasha data</div>`;
132
+
133
+ const periods = this.collectPeriods(d);
134
+ const maxYears = periods.length
135
+ ? Math.max(...periods.map((p) => p.durationYears ?? p.years ?? 1))
136
+ : 0;
137
+
138
+ return html`<div class="wrap" aria-label="Dasha timeline">
139
+ <header class="head">
140
+ <h2 class="title">
141
+ ${
142
+ this.period === 'major'
143
+ ? 'Vimshottari Mahadasha'
144
+ : this.period === 'sub'
145
+ ? 'Antardasha'
146
+ : 'Active dashas'
147
+ }
148
+ </h2>
149
+ ${
150
+ d.nakshatraName || d.moonNakshatra
151
+ ? html`<div class="nakshatra">
152
+ Moon nakshatra: ${d.nakshatraName ?? d.moonNakshatra}
153
+ ${d.nakshatraLord ? html`(lord ${d.nakshatraLord})` : nothing}
154
+ </div>`
155
+ : nothing
156
+ }
157
+ </header>
158
+
159
+ ${this.period === 'current' ? this.renderCurrent(d) : nothing}
160
+ ${
161
+ periods.length > 0
162
+ ? html`<div class="timeline" role="list">
163
+ ${periods.map((p) => this.renderBar(p, maxYears))}
164
+ </div>`
165
+ : nothing
166
+ }
167
+ </div>`;
168
+ }
169
+
170
+ private renderCurrent(d: DashaData) {
171
+ return html`<div class="current">
172
+ ${
173
+ d.mahadasha
174
+ ? html`<div>
175
+ <span>Mahadasha</span>
176
+ <strong>${d.mahadasha.lord ?? d.mahadasha.mahadashaLord}</strong>
177
+ ${
178
+ typeof d.remainingInMahadasha === 'number'
179
+ ? html`<small>${d.remainingInMahadasha.toFixed(1)} years left</small>`
180
+ : nothing
181
+ }
182
+ </div>`
183
+ : nothing
184
+ }
185
+ ${
186
+ d.antardasha
187
+ ? html`<div>
188
+ <span>Antardasha</span>
189
+ <strong>${d.antardasha.lord ?? d.antardasha.antardashaLord}</strong>
190
+ ${
191
+ typeof d.remainingInAntardasha === 'number'
192
+ ? html`<small>${d.remainingInAntardasha.toFixed(1)} years left</small>`
193
+ : nothing
194
+ }
195
+ </div>`
196
+ : nothing
197
+ }
198
+ ${
199
+ d.pratyantardasha
200
+ ? html`<div>
201
+ <span>Pratyantardasha</span>
202
+ <strong
203
+ >${
204
+ d.pratyantardasha.lord ?? d.pratyantardasha.pratyantardashaLord
205
+ }</strong
206
+ >
207
+ ${
208
+ typeof d.remainingInPratyantardasha === 'number'
209
+ ? html`<small
210
+ >${d.remainingInPratyantardasha.toFixed(2)} years left</small
211
+ >`
212
+ : nothing
213
+ }
214
+ </div>`
215
+ : nothing
216
+ }
217
+ </div>`;
218
+ }
219
+
220
+ private collectPeriods(d: DashaData): DashaPeriod[] {
221
+ if (this.period === 'major' && d.mahadashas?.length) return d.mahadashas;
222
+ if (this.period === 'sub' && d.antardashas?.length) return d.antardashas;
223
+ return d.mahadashas ?? d.antardashas ?? [];
224
+ }
225
+
226
+ private renderBar(p: DashaPeriod, max: number) {
227
+ const lord =
228
+ p.lord ?? p.mahadashaLord ?? p.antardashaLord ?? p.planet ?? '';
229
+ const years = p.durationYears ?? p.years ?? 0;
230
+ const width = max > 0 ? (years / max) * 100 : 0;
231
+ return html`<div class="bar" role="listitem">
232
+ <span>${lord}</span>
233
+ <span class="bar-track"><span style="width: ${width}%"></span></span>
234
+ <span class="dates">
235
+ ${p.startDate ? formatYear(p.startDate) : ''}
236
+ ${p.endDate ? html`- ${formatYear(p.endDate)}` : ''}
237
+ </span>
238
+ </div>`;
239
+ }
240
+ }
241
+
242
+ function formatYear(s: string): string {
243
+ const m = s.match(/^(\d{4})/);
244
+ return m ? m[1] : s;
245
+ }
246
+
247
+ declare global {
248
+ interface HTMLElementTagNameMap {
249
+ 'roxy-dasha-timeline': RoxyDashaTimeline;
250
+ }
251
+ }