@roxyapi/ui 0.9.0 → 0.11.0

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 (378) hide show
  1. package/AGENTS.md +53 -27
  2. package/README.md +42 -12
  3. package/components-catalog.json +1022 -0
  4. package/dist/cdn/components/angel-number-card.js +52 -0
  5. package/dist/cdn/components/angel-number-card.js.map +7 -0
  6. package/dist/cdn/components/angel-number-lookup.js +52 -0
  7. package/dist/cdn/components/angel-number-lookup.js.map +7 -0
  8. package/dist/cdn/components/ashtakavarga-grid.js +10 -3
  9. package/dist/cdn/components/ashtakavarga-grid.js.map +4 -4
  10. package/dist/cdn/components/aspects-table.js +52 -0
  11. package/dist/cdn/components/aspects-table.js.map +7 -0
  12. package/dist/cdn/components/biorhythm-chart.js +10 -3
  13. package/dist/cdn/components/biorhythm-chart.js.map +4 -4
  14. package/dist/cdn/components/bodygraph.js +17 -10
  15. package/dist/cdn/components/bodygraph.js.map +4 -4
  16. package/dist/cdn/components/choghadiya-grid.js +10 -3
  17. package/dist/cdn/components/choghadiya-grid.js.map +4 -4
  18. package/dist/cdn/components/compatibility-card.js +10 -3
  19. package/dist/cdn/components/compatibility-card.js.map +4 -4
  20. package/dist/cdn/components/crystal-card.js +52 -0
  21. package/dist/cdn/components/crystal-card.js.map +7 -0
  22. package/dist/cdn/components/crystal-grid.js +52 -0
  23. package/dist/cdn/components/crystal-grid.js.map +7 -0
  24. package/dist/cdn/components/dasha-timeline.js +10 -3
  25. package/dist/cdn/components/dasha-timeline.js.map +4 -4
  26. package/dist/cdn/components/data.js +10 -3
  27. package/dist/cdn/components/data.js.map +4 -4
  28. package/dist/cdn/components/divisional-chart.js +31 -24
  29. package/dist/cdn/components/divisional-chart.js.map +4 -4
  30. package/dist/cdn/components/dosha-card.js +10 -3
  31. package/dist/cdn/components/dosha-card.js.map +4 -4
  32. package/dist/cdn/components/dream-card.js +52 -0
  33. package/dist/cdn/components/dream-card.js.map +7 -0
  34. package/dist/cdn/components/dream-search.js +52 -0
  35. package/dist/cdn/components/dream-search.js.map +7 -0
  36. package/dist/cdn/components/endpoint-form.js +3 -3
  37. package/dist/cdn/components/endpoint-form.js.map +3 -3
  38. package/dist/cdn/components/forecast-digest.js +52 -0
  39. package/dist/cdn/components/forecast-digest.js.map +7 -0
  40. package/dist/cdn/components/forecast-timeline.js +10 -3
  41. package/dist/cdn/components/forecast-timeline.js.map +4 -4
  42. package/dist/cdn/components/guna-milan.js +10 -3
  43. package/dist/cdn/components/guna-milan.js.map +4 -4
  44. package/dist/cdn/components/hd-connection.js +52 -0
  45. package/dist/cdn/components/hd-connection.js.map +7 -0
  46. package/dist/cdn/components/hd-penta.js +52 -0
  47. package/dist/cdn/components/hd-penta.js.map +7 -0
  48. package/dist/cdn/components/hd-variables.js +52 -0
  49. package/dist/cdn/components/hd-variables.js.map +7 -0
  50. package/dist/cdn/components/hexagram.js +10 -3
  51. package/dist/cdn/components/hexagram.js.map +4 -4
  52. package/dist/cdn/components/hora-table.js +52 -0
  53. package/dist/cdn/components/hora-table.js.map +7 -0
  54. package/dist/cdn/components/horoscope-card.js +10 -3
  55. package/dist/cdn/components/horoscope-card.js.map +4 -4
  56. package/dist/cdn/components/kp-chart.js +10 -3
  57. package/dist/cdn/components/kp-chart.js.map +4 -4
  58. package/dist/cdn/components/kp-planets-table.js +10 -3
  59. package/dist/cdn/components/kp-planets-table.js.map +4 -4
  60. package/dist/cdn/components/kp-ruling-planets.js +10 -3
  61. package/dist/cdn/components/kp-ruling-planets.js.map +4 -4
  62. package/dist/cdn/components/location-search.js +3 -3
  63. package/dist/cdn/components/location-search.js.map +2 -2
  64. package/dist/cdn/components/moon-phase.js +10 -3
  65. package/dist/cdn/components/moon-phase.js.map +4 -4
  66. package/dist/cdn/components/nakshatra-card.js +10 -3
  67. package/dist/cdn/components/nakshatra-card.js.map +4 -4
  68. package/dist/cdn/components/natal-chart.js +16 -9
  69. package/dist/cdn/components/natal-chart.js.map +4 -4
  70. package/dist/cdn/components/numerology-card.js +10 -3
  71. package/dist/cdn/components/numerology-card.js.map +4 -4
  72. package/dist/cdn/components/panchang-table.js +10 -3
  73. package/dist/cdn/components/panchang-table.js.map +4 -4
  74. package/dist/cdn/components/reference-card.js +52 -0
  75. package/dist/cdn/components/reference-card.js.map +7 -0
  76. package/dist/cdn/components/shadbala-table.js +10 -3
  77. package/dist/cdn/components/shadbala-table.js.map +4 -4
  78. package/dist/cdn/components/synastry-chart.js +16 -9
  79. package/dist/cdn/components/synastry-chart.js.map +4 -4
  80. package/dist/cdn/components/tarot-card.js +10 -3
  81. package/dist/cdn/components/tarot-card.js.map +4 -4
  82. package/dist/cdn/components/tarot-catalog.js +52 -0
  83. package/dist/cdn/components/tarot-catalog.js.map +7 -0
  84. package/dist/cdn/components/tarot-spread.js +10 -3
  85. package/dist/cdn/components/tarot-spread.js.map +4 -4
  86. package/dist/cdn/components/transits-table.js +10 -3
  87. package/dist/cdn/components/transits-table.js.map +4 -4
  88. package/dist/cdn/components/vedic-aspects.js +52 -0
  89. package/dist/cdn/components/vedic-aspects.js.map +7 -0
  90. package/dist/cdn/components/vedic-kundli.js +31 -24
  91. package/dist/cdn/components/vedic-kundli.js.map +4 -4
  92. package/dist/cdn/components/vedic-planets-table.js +10 -3
  93. package/dist/cdn/components/vedic-planets-table.js.map +4 -4
  94. package/dist/cdn/components/western-planets-table.js +10 -3
  95. package/dist/cdn/components/western-planets-table.js.map +4 -4
  96. package/dist/cdn/components/yoga-list.js +10 -3
  97. package/dist/cdn/components/yoga-list.js.map +4 -4
  98. package/dist/cdn/roxy-ui.js +92 -81
  99. package/dist/cdn/roxy-ui.js.map +4 -4
  100. package/dist/components/angel-number-card.d.ts +17 -0
  101. package/dist/components/angel-number-card.d.ts.map +1 -0
  102. package/dist/components/angel-number-card.js +2 -0
  103. package/dist/components/angel-number-card.js.map +7 -0
  104. package/dist/components/angel-number-lookup.d.ts +21 -0
  105. package/dist/components/angel-number-lookup.d.ts.map +1 -0
  106. package/dist/components/angel-number-lookup.js +2 -0
  107. package/dist/components/angel-number-lookup.js.map +7 -0
  108. package/dist/components/ashtakavarga-grid.d.ts +4 -5
  109. package/dist/components/ashtakavarga-grid.d.ts.map +1 -1
  110. package/dist/components/ashtakavarga-grid.js +1 -1
  111. package/dist/components/ashtakavarga-grid.js.map +4 -4
  112. package/dist/components/aspects-table.d.ts +23 -0
  113. package/dist/components/aspects-table.d.ts.map +1 -0
  114. package/dist/components/aspects-table.js +2 -0
  115. package/dist/components/aspects-table.js.map +7 -0
  116. package/dist/components/biorhythm-chart.d.ts +4 -5
  117. package/dist/components/biorhythm-chart.d.ts.map +1 -1
  118. package/dist/components/biorhythm-chart.js +1 -1
  119. package/dist/components/biorhythm-chart.js.map +4 -4
  120. package/dist/components/bodygraph.d.ts +4 -5
  121. package/dist/components/bodygraph.d.ts.map +1 -1
  122. package/dist/components/bodygraph.js +9 -9
  123. package/dist/components/bodygraph.js.map +4 -4
  124. package/dist/components/choghadiya-grid.d.ts +4 -5
  125. package/dist/components/choghadiya-grid.d.ts.map +1 -1
  126. package/dist/components/choghadiya-grid.js +1 -1
  127. package/dist/components/choghadiya-grid.js.map +4 -4
  128. package/dist/components/compatibility-card.d.ts +4 -5
  129. package/dist/components/compatibility-card.d.ts.map +1 -1
  130. package/dist/components/compatibility-card.js +1 -1
  131. package/dist/components/compatibility-card.js.map +4 -4
  132. package/dist/components/crystal-card.d.ts +19 -0
  133. package/dist/components/crystal-card.d.ts.map +1 -0
  134. package/dist/components/crystal-card.js +2 -0
  135. package/dist/components/crystal-card.js.map +7 -0
  136. package/dist/components/crystal-grid.d.ts +26 -0
  137. package/dist/components/crystal-grid.d.ts.map +1 -0
  138. package/dist/components/crystal-grid.js +2 -0
  139. package/dist/components/crystal-grid.js.map +7 -0
  140. package/dist/components/dasha-timeline.d.ts +4 -5
  141. package/dist/components/dasha-timeline.d.ts.map +1 -1
  142. package/dist/components/dasha-timeline.js +1 -1
  143. package/dist/components/dasha-timeline.js.map +4 -4
  144. package/dist/components/data.d.ts +5 -8
  145. package/dist/components/data.d.ts.map +1 -1
  146. package/dist/components/data.js +1 -1
  147. package/dist/components/data.js.map +4 -4
  148. package/dist/components/divisional-chart.d.ts +4 -5
  149. package/dist/components/divisional-chart.d.ts.map +1 -1
  150. package/dist/components/divisional-chart.js +59 -59
  151. package/dist/components/divisional-chart.js.map +4 -4
  152. package/dist/components/dosha-card.d.ts +4 -5
  153. package/dist/components/dosha-card.d.ts.map +1 -1
  154. package/dist/components/dosha-card.js +1 -1
  155. package/dist/components/dosha-card.js.map +4 -4
  156. package/dist/components/dream-card.d.ts +16 -0
  157. package/dist/components/dream-card.d.ts.map +1 -0
  158. package/dist/components/dream-card.js +2 -0
  159. package/dist/components/dream-card.js.map +7 -0
  160. package/dist/components/dream-search.d.ts +18 -0
  161. package/dist/components/dream-search.d.ts.map +1 -0
  162. package/dist/components/dream-search.js +2 -0
  163. package/dist/components/dream-search.js.map +7 -0
  164. package/dist/components/endpoint-form.d.ts +10 -5
  165. package/dist/components/endpoint-form.d.ts.map +1 -1
  166. package/dist/components/endpoint-form.js +1 -1
  167. package/dist/components/endpoint-form.js.map +3 -3
  168. package/dist/components/forecast-digest.d.ts +19 -0
  169. package/dist/components/forecast-digest.d.ts.map +1 -0
  170. package/dist/components/forecast-digest.js +2 -0
  171. package/dist/components/forecast-digest.js.map +7 -0
  172. package/dist/components/forecast-timeline.d.ts +8 -6
  173. package/dist/components/forecast-timeline.d.ts.map +1 -1
  174. package/dist/components/forecast-timeline.js +1 -1
  175. package/dist/components/forecast-timeline.js.map +4 -4
  176. package/dist/components/guna-milan.d.ts +4 -5
  177. package/dist/components/guna-milan.d.ts.map +1 -1
  178. package/dist/components/guna-milan.js +1 -1
  179. package/dist/components/guna-milan.js.map +4 -4
  180. package/dist/components/hd-connection.d.ts +18 -0
  181. package/dist/components/hd-connection.d.ts.map +1 -0
  182. package/dist/components/hd-connection.js +2 -0
  183. package/dist/components/hd-connection.js.map +7 -0
  184. package/dist/components/hd-penta.d.ts +18 -0
  185. package/dist/components/hd-penta.d.ts.map +1 -0
  186. package/dist/components/hd-penta.js +2 -0
  187. package/dist/components/hd-penta.js.map +7 -0
  188. package/dist/components/hd-variables.d.ts +17 -0
  189. package/dist/components/hd-variables.d.ts.map +1 -0
  190. package/dist/components/hd-variables.js +2 -0
  191. package/dist/components/hd-variables.js.map +7 -0
  192. package/dist/components/hexagram.d.ts +4 -5
  193. package/dist/components/hexagram.d.ts.map +1 -1
  194. package/dist/components/hexagram.js +1 -1
  195. package/dist/components/hexagram.js.map +4 -4
  196. package/dist/components/hora-table.d.ts +17 -0
  197. package/dist/components/hora-table.d.ts.map +1 -0
  198. package/dist/components/hora-table.js +2 -0
  199. package/dist/components/hora-table.js.map +7 -0
  200. package/dist/components/horoscope-card.d.ts +4 -5
  201. package/dist/components/horoscope-card.d.ts.map +1 -1
  202. package/dist/components/horoscope-card.js +1 -1
  203. package/dist/components/horoscope-card.js.map +4 -4
  204. package/dist/components/kp-chart.d.ts +4 -5
  205. package/dist/components/kp-chart.d.ts.map +1 -1
  206. package/dist/components/kp-chart.js +1 -1
  207. package/dist/components/kp-chart.js.map +4 -4
  208. package/dist/components/kp-planets-table.d.ts +4 -5
  209. package/dist/components/kp-planets-table.d.ts.map +1 -1
  210. package/dist/components/kp-planets-table.js +1 -1
  211. package/dist/components/kp-planets-table.js.map +4 -4
  212. package/dist/components/kp-ruling-planets.d.ts +4 -5
  213. package/dist/components/kp-ruling-planets.d.ts.map +1 -1
  214. package/dist/components/kp-ruling-planets.js +1 -1
  215. package/dist/components/kp-ruling-planets.js.map +4 -4
  216. package/dist/components/location-search.js +1 -1
  217. package/dist/components/location-search.js.map +2 -2
  218. package/dist/components/moon-phase.d.ts +5 -5
  219. package/dist/components/moon-phase.d.ts.map +1 -1
  220. package/dist/components/moon-phase.js +1 -1
  221. package/dist/components/moon-phase.js.map +4 -4
  222. package/dist/components/nakshatra-card.d.ts +4 -5
  223. package/dist/components/nakshatra-card.d.ts.map +1 -1
  224. package/dist/components/nakshatra-card.js +1 -1
  225. package/dist/components/nakshatra-card.js.map +4 -4
  226. package/dist/components/natal-chart.d.ts +4 -5
  227. package/dist/components/natal-chart.d.ts.map +1 -1
  228. package/dist/components/natal-chart.js +8 -8
  229. package/dist/components/natal-chart.js.map +4 -4
  230. package/dist/components/numerology-card.d.ts +15 -10
  231. package/dist/components/numerology-card.d.ts.map +1 -1
  232. package/dist/components/numerology-card.js +1 -1
  233. package/dist/components/numerology-card.js.map +4 -4
  234. package/dist/components/panchang-table.d.ts +4 -5
  235. package/dist/components/panchang-table.d.ts.map +1 -1
  236. package/dist/components/panchang-table.js +1 -1
  237. package/dist/components/panchang-table.js.map +4 -4
  238. package/dist/components/reference-card.d.ts +19 -0
  239. package/dist/components/reference-card.d.ts.map +1 -0
  240. package/dist/components/reference-card.js +2 -0
  241. package/dist/components/reference-card.js.map +7 -0
  242. package/dist/components/shadbala-table.d.ts +4 -5
  243. package/dist/components/shadbala-table.d.ts.map +1 -1
  244. package/dist/components/shadbala-table.js +1 -1
  245. package/dist/components/shadbala-table.js.map +4 -4
  246. package/dist/components/synastry-chart.d.ts +4 -5
  247. package/dist/components/synastry-chart.d.ts.map +1 -1
  248. package/dist/components/synastry-chart.js +7 -7
  249. package/dist/components/synastry-chart.js.map +4 -4
  250. package/dist/components/tarot-card.d.ts +4 -5
  251. package/dist/components/tarot-card.d.ts.map +1 -1
  252. package/dist/components/tarot-card.js +1 -1
  253. package/dist/components/tarot-card.js.map +4 -4
  254. package/dist/components/tarot-catalog.d.ts +20 -0
  255. package/dist/components/tarot-catalog.d.ts.map +1 -0
  256. package/dist/components/tarot-catalog.js +2 -0
  257. package/dist/components/tarot-catalog.js.map +7 -0
  258. package/dist/components/tarot-spread.d.ts +7 -8
  259. package/dist/components/tarot-spread.d.ts.map +1 -1
  260. package/dist/components/tarot-spread.js +1 -1
  261. package/dist/components/tarot-spread.js.map +4 -4
  262. package/dist/components/transits-table.d.ts +4 -5
  263. package/dist/components/transits-table.d.ts.map +1 -1
  264. package/dist/components/transits-table.js +1 -1
  265. package/dist/components/transits-table.js.map +4 -4
  266. package/dist/components/vedic-aspects.d.ts +16 -0
  267. package/dist/components/vedic-aspects.d.ts.map +1 -0
  268. package/dist/components/vedic-aspects.js +2 -0
  269. package/dist/components/vedic-aspects.js.map +7 -0
  270. package/dist/components/vedic-kundli.d.ts +22 -5
  271. package/dist/components/vedic-kundli.d.ts.map +1 -1
  272. package/dist/components/vedic-kundli.js +63 -63
  273. package/dist/components/vedic-kundli.js.map +4 -4
  274. package/dist/components/vedic-planets-table.d.ts +4 -5
  275. package/dist/components/vedic-planets-table.d.ts.map +1 -1
  276. package/dist/components/vedic-planets-table.js +1 -1
  277. package/dist/components/vedic-planets-table.js.map +4 -4
  278. package/dist/components/western-planets-table.d.ts +4 -5
  279. package/dist/components/western-planets-table.d.ts.map +1 -1
  280. package/dist/components/western-planets-table.js +1 -1
  281. package/dist/components/western-planets-table.js.map +4 -4
  282. package/dist/components/yoga-list.d.ts +6 -7
  283. package/dist/components/yoga-list.d.ts.map +1 -1
  284. package/dist/components/yoga-list.js +1 -1
  285. package/dist/components/yoga-list.js.map +4 -4
  286. package/dist/generated/endpoint-bindings.d.ts +15 -0
  287. package/dist/generated/endpoint-bindings.d.ts.map +1 -0
  288. package/dist/index.cjs +73 -73
  289. package/dist/index.cjs.map +4 -4
  290. package/dist/index.d.ts +16 -0
  291. package/dist/index.d.ts.map +1 -1
  292. package/dist/index.js +71 -71
  293. package/dist/index.js.map +4 -4
  294. package/dist/manifest.d.ts.map +1 -1
  295. package/dist/manifest.json +39 -24
  296. package/dist/styles/tokens-css.d.ts +1 -1
  297. package/dist/styles/tokens-css.d.ts.map +1 -1
  298. package/dist/styles/tokens.css +4 -0
  299. package/dist/types/index.d.ts +1 -1
  300. package/dist/types/index.d.ts.map +1 -1
  301. package/dist/types/types.gen.d.ts +1103 -164
  302. package/dist/types/types.gen.d.ts.map +1 -1
  303. package/dist/utils/angel-sections.d.ts +14 -0
  304. package/dist/utils/angel-sections.d.ts.map +1 -0
  305. package/dist/utils/base-element.d.ts +73 -0
  306. package/dist/utils/base-element.d.ts.map +1 -0
  307. package/dist/utils/base-styles.d.ts.map +1 -1
  308. package/dist/utils/fetch-controller.d.ts +60 -0
  309. package/dist/utils/fetch-controller.d.ts.map +1 -0
  310. package/dist/utils/kundli-render.d.ts +2 -1
  311. package/dist/utils/kundli-render.d.ts.map +1 -1
  312. package/dist/utils/kundli-styles.d.ts.map +1 -1
  313. package/dist/utils/markup-data.d.ts +34 -0
  314. package/dist/utils/markup-data.d.ts.map +1 -1
  315. package/dist/version.d.ts +1 -1
  316. package/dist/version.d.ts.map +1 -1
  317. package/package.json +2 -1
  318. package/src/components/angel-number-card.ts +210 -0
  319. package/src/components/angel-number-lookup.ts +207 -0
  320. package/src/components/ashtakavarga-grid.ts +15 -20
  321. package/src/components/aspects-table.ts +329 -0
  322. package/src/components/biorhythm-chart.ts +14 -18
  323. package/src/components/bodygraph.ts +9 -20
  324. package/src/components/choghadiya-grid.ts +15 -19
  325. package/src/components/compatibility-card.ts +9 -19
  326. package/src/components/crystal-card.ts +242 -0
  327. package/src/components/crystal-grid.ts +182 -0
  328. package/src/components/dasha-timeline.ts +9 -20
  329. package/src/components/data.ts +8 -27
  330. package/src/components/divisional-chart.ts +10 -18
  331. package/src/components/dosha-card.ts +8 -19
  332. package/src/components/dream-card.ts +88 -0
  333. package/src/components/dream-search.ts +135 -0
  334. package/src/components/endpoint-form.ts +149 -55
  335. package/src/components/forecast-digest.ts +213 -0
  336. package/src/components/forecast-timeline.ts +24 -19
  337. package/src/components/guna-milan.ts +8 -19
  338. package/src/components/hd-connection.ts +188 -0
  339. package/src/components/hd-penta.ts +165 -0
  340. package/src/components/hd-variables.ts +128 -0
  341. package/src/components/hexagram.ts +10 -18
  342. package/src/components/hora-table.ts +149 -0
  343. package/src/components/horoscope-card.ts +9 -19
  344. package/src/components/kp-chart.ts +11 -20
  345. package/src/components/kp-planets-table.ts +11 -20
  346. package/src/components/kp-ruling-planets.ts +8 -18
  347. package/src/components/moon-phase.ts +10 -19
  348. package/src/components/nakshatra-card.ts +11 -20
  349. package/src/components/natal-chart.ts +20 -23
  350. package/src/components/numerology-card.ts +90 -31
  351. package/src/components/panchang-table.ts +9 -19
  352. package/src/components/reference-card.ts +212 -0
  353. package/src/components/shadbala-table.ts +15 -18
  354. package/src/components/synastry-chart.ts +17 -20
  355. package/src/components/tarot-card.ts +9 -20
  356. package/src/components/tarot-catalog.ts +129 -0
  357. package/src/components/tarot-spread.ts +20 -21
  358. package/src/components/transits-table.ts +17 -20
  359. package/src/components/vedic-aspects.ts +188 -0
  360. package/src/components/vedic-kundli.ts +46 -19
  361. package/src/components/vedic-planets-table.ts +11 -19
  362. package/src/components/western-planets-table.ts +11 -19
  363. package/src/components/yoga-list.ts +17 -23
  364. package/src/generated/endpoint-bindings.ts +655 -0
  365. package/src/index.ts +27 -0
  366. package/src/manifest.ts +198 -6
  367. package/src/styles/tokens-css.ts +4 -0
  368. package/src/styles/tokens.css +4 -0
  369. package/src/types/index.ts +1 -1
  370. package/src/types/types.gen.ts +1123 -164
  371. package/src/utils/angel-sections.ts +38 -0
  372. package/src/utils/base-element.ts +183 -0
  373. package/src/utils/base-styles.ts +33 -0
  374. package/src/utils/fetch-controller.ts +166 -0
  375. package/src/utils/kundli-render.ts +9 -2
  376. package/src/utils/kundli-styles.ts +7 -1
  377. package/src/utils/markup-data.ts +45 -0
  378. package/src/version.ts +1 -1
@@ -22,7 +22,12 @@ interface OpenApiSchema extends OpenApiSchemaRef {
22
22
  }
23
23
 
24
24
  interface FieldDef {
25
+ /** Unique storage + input key: the property name, or "group.name" for a nested object field. */
26
+ key: string;
27
+ /** Schema property name (the label source), e.g. "date". */
25
28
  name: string;
29
+ /** Parent object key for a nested schema (person1/person2); undefined for a flat field. */
30
+ group?: string;
26
31
  type: string;
27
32
  required: boolean;
28
33
  description?: string;
@@ -66,9 +71,10 @@ async function loadSpec(url: string): Promise<OpenApiDoc> {
66
71
  * `roxy-submit` CustomEvent with the validated payload on submit. The caller
67
72
  * decides what to do (call the SDK, render a chart, navigate).
68
73
  *
69
- * Build-time hints (x-roxy-ui formGroups) are read by scripts/build.ts and
70
- * baked into a static map. At runtime the component falls back to runtime
71
- * fetch of /api/v2/openapi.json when no map is provided.
74
+ * At runtime the component fetches the OpenAPI spec (its `spec-url`, default
75
+ * /api/v2/openapi.json) and builds the fields from the operation's request
76
+ * schema: types, enums, required flags, and property order all come from the
77
+ * spec, so a new endpoint gets a working form with no per-endpoint code.
72
78
  */
73
79
  @customElement('roxy-endpoint-form')
74
80
  export class RoxyEndpointForm extends LitElement {
@@ -95,6 +101,20 @@ export class RoxyEndpointForm extends LitElement {
95
101
  align-items: start;
96
102
  gap: var(--roxy-space-md, 1rem);
97
103
  }
104
+ .person-group {
105
+ border: 1px solid var(--roxy-border, #e4e4e7);
106
+ border-radius: var(--roxy-radius-md, 8px);
107
+ padding: var(--roxy-space-md, 1rem);
108
+ margin: 0 0 var(--roxy-space-md, 1rem);
109
+ display: grid;
110
+ gap: var(--roxy-space-md, 1rem);
111
+ min-inline-size: 0;
112
+ }
113
+ .person-group legend {
114
+ padding: 0 var(--roxy-space-xs, 0.25rem);
115
+ font-weight: var(--roxy-weight-bold, 600);
116
+ text-transform: capitalize;
117
+ }
98
118
  .field {
99
119
  display: flex;
100
120
  flex-direction: column;
@@ -191,15 +211,16 @@ export class RoxyEndpointForm extends LitElement {
191
211
  @property({ type: String, attribute: 'submit-label' })
192
212
  submitLabel = 'Submit';
193
213
 
214
+ /** Prefill values, keyed by field name. Used by `remember` mode to restore the last submission. JS property only. */
215
+ @property({ attribute: false })
216
+ initialValues?: Record<string, unknown>;
217
+
194
218
  @state()
195
219
  private fields: FieldDef[] = [];
196
220
 
197
221
  @state()
198
222
  private values: Record<string, unknown> = {};
199
223
 
200
- @state()
201
- private hasLocation = false;
202
-
203
224
  @state()
204
225
  private loaded = false;
205
226
 
@@ -251,16 +272,40 @@ export class RoxyEndpointForm extends LitElement {
251
272
  const required = new Set(bodySchema.required ?? []);
252
273
  for (const [name, sub] of Object.entries(bodySchema.properties)) {
253
274
  const resolved = this.resolve(sub, schemas) ?? {};
254
- fields.push({
255
- name,
256
- type: this.fieldType(resolved),
257
- required: required.has(name),
258
- description: resolved.description,
259
- enum: resolved.enum,
260
- min: resolved.minimum,
261
- max: resolved.maximum,
262
- default: resolved.default,
263
- });
275
+ // Nested object (e.g. person1/person2 on synastry / guna-milan):
276
+ // expand into a labelled group of sub-fields keyed "group.sub".
277
+ if (resolved.type === 'object' && resolved.properties) {
278
+ const subRequired = new Set(resolved.required ?? []);
279
+ for (const [subName, subSchema] of Object.entries(
280
+ resolved.properties,
281
+ )) {
282
+ const r = this.resolve(subSchema, schemas) ?? {};
283
+ fields.push({
284
+ key: `${name}.${subName}`,
285
+ name: subName,
286
+ group: name,
287
+ type: this.fieldType(r),
288
+ required: required.has(name) && subRequired.has(subName),
289
+ description: r.description,
290
+ enum: r.enum,
291
+ min: r.minimum,
292
+ max: r.maximum,
293
+ default: r.default,
294
+ });
295
+ }
296
+ } else {
297
+ fields.push({
298
+ key: name,
299
+ name,
300
+ type: this.fieldType(resolved),
301
+ required: required.has(name),
302
+ description: resolved.description,
303
+ enum: resolved.enum,
304
+ min: resolved.minimum,
305
+ max: resolved.maximum,
306
+ default: resolved.default,
307
+ });
308
+ }
264
309
  }
265
310
  }
266
311
 
@@ -268,6 +313,7 @@ export class RoxyEndpointForm extends LitElement {
268
313
  if (param.in === 'path' || param.in === 'query') {
269
314
  const resolved = this.resolve(param.schema, schemas) ?? {};
270
315
  fields.push({
316
+ key: param.name,
271
317
  name: param.name,
272
318
  type: this.fieldType(resolved),
273
319
  required: !!param.required,
@@ -279,15 +325,22 @@ export class RoxyEndpointForm extends LitElement {
279
325
  }
280
326
 
281
327
  this.fields = fields;
282
- this.hasLocation =
283
- fields.some((f) => f.name === 'latitude') &&
284
- fields.some((f) => f.name === 'longitude') &&
285
- fields.some((f) => f.name === 'timezone');
286
328
 
287
- // Pre-fill defaults
329
+ // Pre-fill: schema defaults first, then any remembered/initial values
330
+ // (only for fields this endpoint has) so a stale stored payload from
331
+ // another endpoint cannot inject unknown keys. Remembered values are the
332
+ // previously-submitted shape (nested per group), so read them by group.
288
333
  const init: Record<string, unknown> = {};
289
334
  for (const f of fields) {
290
- if (f.default !== undefined) init[f.name] = f.default;
335
+ if (f.default !== undefined) init[f.key] = f.default;
336
+ const remembered = f.group
337
+ ? (
338
+ this.initialValues?.[f.group] as
339
+ | Record<string, unknown>
340
+ | undefined
341
+ )?.[f.name]
342
+ : this.initialValues?.[f.name];
343
+ if (remembered !== undefined) init[f.key] = remembered;
291
344
  }
292
345
  this.values = init;
293
346
  this.loaded = true;
@@ -336,43 +389,67 @@ export class RoxyEndpointForm extends LitElement {
336
389
  this.values = { ...this.values, [name]: value };
337
390
  }
338
391
 
339
- private onLocation = (e: Event) => {
340
- const detail = (e as CustomEvent).detail as {
341
- latitude?: number;
342
- longitude?: number;
343
- timezone?: string;
344
- utcOffset?: number;
345
- };
346
- if (detail) {
392
+ /** True when the fields in `group` (or the flat top level) carry latitude+longitude+timezone, so a location-search can autofill them. */
393
+ private groupHasLocation(group?: string): boolean {
394
+ const inGroup = this.fields.filter((f) => f.group === group);
395
+ return (['latitude', 'longitude', 'timezone'] as const).every((n) =>
396
+ inGroup.some((f) => f.name === n),
397
+ );
398
+ }
399
+
400
+ /** Location-select handler bound to a group: fills that group's lat/lng/timezone keys (flat top level when group is undefined). */
401
+ private onLocationFor(group?: string) {
402
+ const prefix = group ? `${group}.` : '';
403
+ return (e: Event) => {
404
+ const detail = (e as CustomEvent).detail as {
405
+ latitude?: number;
406
+ longitude?: number;
407
+ timezone?: string;
408
+ utcOffset?: number;
409
+ };
410
+ if (!detail) return;
347
411
  this.values = {
348
412
  ...this.values,
349
- latitude: detail.latitude,
350
- longitude: detail.longitude,
351
- timezone: detail.timezone ?? detail.utcOffset,
413
+ [`${prefix}latitude`]: detail.latitude,
414
+ [`${prefix}longitude`]: detail.longitude,
415
+ [`${prefix}timezone`]: detail.timezone ?? detail.utcOffset,
352
416
  };
353
- }
354
- };
417
+ };
418
+ }
355
419
 
356
420
  private onSubmit = (e: Event) => {
357
421
  e.preventDefault();
358
422
  const missing = this.fields
359
423
  .filter((f) => f.required)
360
424
  .filter(
361
- (f) => this.values[f.name] === undefined || this.values[f.name] === '',
425
+ (f) => this.values[f.key] === undefined || this.values[f.key] === '',
362
426
  );
363
427
  if (missing.length > 0) {
364
428
  this.dispatchEvent(
365
429
  new CustomEvent('roxy-validation-error', {
366
- detail: { missing: missing.map((m) => m.name) },
430
+ detail: { missing: missing.map((m) => m.key) },
367
431
  bubbles: true,
368
432
  composed: true,
369
433
  }),
370
434
  );
371
435
  return;
372
436
  }
437
+ // Reconstruct the request shape: grouped fields nest as { group: { name: value } }.
438
+ const out: Record<string, unknown> = {};
439
+ for (const f of this.fields) {
440
+ const v = this.values[f.key];
441
+ if (v === undefined || v === '') continue;
442
+ if (f.group) {
443
+ const g = (out[f.group] as Record<string, unknown> | undefined) ?? {};
444
+ g[f.name] = v;
445
+ out[f.group] = g;
446
+ } else {
447
+ out[f.name] = v;
448
+ }
449
+ }
373
450
  this.dispatchEvent(
374
451
  new CustomEvent('roxy-submit', {
375
- detail: { endpoint: this.endpoint, values: this.values },
452
+ detail: { endpoint: this.endpoint, values: out },
376
453
  bubbles: true,
377
454
  composed: true,
378
455
  }),
@@ -393,14 +470,14 @@ export class RoxyEndpointForm extends LitElement {
393
470
 
394
471
  const renderField = (f: FieldDef) => {
395
472
  if (
396
- this.hasLocation &&
473
+ this.groupHasLocation(f.group) &&
397
474
  (f.name === 'latitude' ||
398
475
  f.name === 'longitude' ||
399
476
  f.name === 'timezone')
400
477
  ) {
401
478
  return nothing;
402
479
  }
403
- const inputId = `roxy-form-${f.name}`;
480
+ const inputId = `roxy-form-${f.key}`;
404
481
  return html`<div class="field">
405
482
  <label for=${inputId}>
406
483
  ${humanize(f.name)}${f.required ? html`<span class="req" aria-hidden="true">*</span>` : nothing}
@@ -410,13 +487,13 @@ export class RoxyEndpointForm extends LitElement {
410
487
  ? html`<select
411
488
  id=${inputId}
412
489
  ?required=${f.required}
413
- @change=${(e: Event) => this.setValue(f.name, (e.target as HTMLSelectElement).value)}
490
+ @change=${(e: Event) => this.setValue(f.key, (e.target as HTMLSelectElement).value)}
414
491
  >
415
492
  <option value="">Choose</option>
416
493
  ${f.enum.map(
417
494
  (
418
495
  opt,
419
- ) => html`<option value=${opt} ?selected=${this.values[f.name] === opt}>
496
+ ) => html`<option value=${opt} ?selected=${this.values[f.key] === opt}>
420
497
  ${opt}
421
498
  </option>`,
422
499
  )}
@@ -428,10 +505,10 @@ export class RoxyEndpointForm extends LitElement {
428
505
  min=${f.min ?? ''}
429
506
  max=${f.max ?? ''}
430
507
  step=${f.type === 'number' ? 'any' : ''}
431
- .value=${(this.values[f.name] ?? '') as string}
508
+ .value=${(this.values[f.key] ?? '') as string}
432
509
  @input=${(e: Event) =>
433
510
  this.setValue(
434
- f.name,
511
+ f.key,
435
512
  this.coerce(f.type, (e.target as HTMLInputElement).value),
436
513
  )}
437
514
  />`
@@ -440,25 +517,42 @@ export class RoxyEndpointForm extends LitElement {
440
517
  </div>`;
441
518
  };
442
519
 
443
- return html`<form @submit=${this.onSubmit}>
444
- <h2 class="title">${humanize(this.endpoint.split('/').pop() ?? '')}</h2>
445
- ${
446
- this.hasLocation
447
- ? html`<div class="location-block">
448
- <label>Birth location</label>
520
+ // Ordered list of field groups: the flat top level (undefined) plus each
521
+ // nested object (person1/person2). Order follows first appearance.
522
+ const groups: (string | undefined)[] = [];
523
+ for (const f of this.fields) {
524
+ if (!groups.includes(f.group)) groups.push(f.group);
525
+ }
526
+
527
+ const locationBlock = (group?: string) =>
528
+ this.groupHasLocation(group)
529
+ ? html`<div class="location-block">
530
+ <label>${group ? `${humanize(group)} location` : 'Birth location'}</label>
449
531
  <roxy-location-search
450
- @roxy-location-select=${this.onLocation}
532
+ @roxy-location-select=${this.onLocationFor(group)}
451
533
  placeholder="City of birth"
452
534
  ></roxy-location-search>
453
535
  <small class="help">
454
536
  Required: latitude, longitude, timezone. Pick a city to autofill.
455
537
  </small>
456
538
  </div>`
457
- : nothing
458
- }
539
+ : nothing;
540
+
541
+ const groupBody = (group?: string) => html`${locationBlock(group)}
459
542
  <div class="fields">
460
- ${this.fields.map((f) => renderField(f))}
461
- </div>
543
+ ${this.fields.filter((f) => f.group === group).map((f) => renderField(f))}
544
+ </div>`;
545
+
546
+ return html`<form @submit=${this.onSubmit}>
547
+ <h2 class="title">${humanize(this.endpoint.split('/').pop() ?? '')}</h2>
548
+ ${groups.map((g) =>
549
+ g === undefined
550
+ ? groupBody(undefined)
551
+ : html`<fieldset class="person-group">
552
+ <legend>${humanize(g)}</legend>
553
+ ${groupBody(g)}
554
+ </fieldset>`,
555
+ )}
462
556
  <button class="submit" type="submit">${this.submitLabel}</button>
463
557
  </form>`;
464
558
  }
@@ -0,0 +1,213 @@
1
+ import { css, html, nothing } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
+ import type { GenerateDigestResponse } from '../types/index.js';
4
+ import { RoxyDataElement } from '../utils/base-element.js';
5
+ import { baseStyles } from '../utils/base-styles.js';
6
+ import { formatDate, formatNumber } from '../utils/format.js';
7
+ import { humanize } from '../utils/string.js';
8
+
9
+ type DigestWindow = NonNullable<GenerateDigestResponse['windows']>[number];
10
+ type DigestEvent = NonNullable<DigestWindow['top']>[number];
11
+
12
+ /**
13
+ * Forecast digest: the rolled-up reading across the next 24 hours, 7, 30, and 90 days. Renders /forecast/digest. Each window shows how many events fall in it, a per-domain breakdown (western, vedic, biorhythm), and the few highest-significance events with a significance bar coloured by domain. Use it as the at-a-glance period summary that pairs with the day-by-day roxy-forecast-timeline.
14
+ */
15
+ @customElement('roxy-forecast-digest')
16
+ export class RoxyForecastDigest extends RoxyDataElement<GenerateDigestResponse> {
17
+ static styles = [
18
+ baseStyles,
19
+ css`
20
+ .wrap {
21
+ background: var(--roxy-surface, #fff);
22
+ color: var(--roxy-fg, #0a0a0a);
23
+ border: 1px solid var(--roxy-border, #e4e4e7);
24
+ border-radius: var(--roxy-radius-md, 8px);
25
+ padding: var(--roxy-space-lg, 1.5rem);
26
+ box-shadow: var(--roxy-shadow-sm);
27
+ display: grid;
28
+ gap: var(--roxy-space-md, 1rem);
29
+ }
30
+ .head {
31
+ display: flex;
32
+ justify-content: space-between;
33
+ align-items: baseline;
34
+ gap: var(--roxy-space-md, 1rem);
35
+ flex-wrap: wrap;
36
+ }
37
+ .title {
38
+ font-size: var(--roxy-text-lg, 1.125rem);
39
+ font-weight: var(--roxy-weight-bold, 600);
40
+ margin: 0;
41
+ }
42
+ .subtitle {
43
+ color: var(--roxy-muted, #71717a);
44
+ font-size: var(--roxy-text-sm, 0.875rem);
45
+ margin: 0;
46
+ }
47
+ .window {
48
+ display: grid;
49
+ gap: var(--roxy-space-sm, 0.5rem);
50
+ padding-top: var(--roxy-space-md, 1rem);
51
+ border-top: 1px solid var(--roxy-border, #e4e4e7);
52
+ }
53
+ .window:first-of-type {
54
+ border-top: none;
55
+ padding-top: 0;
56
+ }
57
+ .window-head {
58
+ display: flex;
59
+ align-items: baseline;
60
+ gap: 0.5rem;
61
+ flex-wrap: wrap;
62
+ }
63
+ .window-label {
64
+ font-weight: var(--roxy-weight-bold, 600);
65
+ }
66
+ .window-count {
67
+ margin-left: auto;
68
+ color: var(--roxy-muted, #71717a);
69
+ font-size: var(--roxy-text-xs, 0.75rem);
70
+ font-variant-numeric: tabular-nums;
71
+ }
72
+ .domains {
73
+ display: flex;
74
+ flex-wrap: wrap;
75
+ gap: 0.4rem;
76
+ }
77
+ .domain-chip {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ gap: 0.35rem;
81
+ font-size: var(--roxy-text-xs, 0.75rem);
82
+ color: var(--roxy-muted, #71717a);
83
+ }
84
+ .swatch {
85
+ width: 0.65rem;
86
+ height: 0.65rem;
87
+ border-radius: 2px;
88
+ display: inline-block;
89
+ }
90
+ .sw-western {
91
+ background: var(--roxy-accent, #f59e0b);
92
+ }
93
+ .sw-vedic {
94
+ background: var(--roxy-info, #2563eb);
95
+ }
96
+ .sw-biorhythm {
97
+ background: var(--roxy-success, #16a34a);
98
+ }
99
+ .event {
100
+ display: grid;
101
+ grid-template-columns: auto 1fr;
102
+ gap: 0.25rem 0.6rem;
103
+ align-items: baseline;
104
+ }
105
+ .event-date {
106
+ color: var(--roxy-muted, #71717a);
107
+ font-size: var(--roxy-text-xs, 0.75rem);
108
+ font-variant-numeric: tabular-nums;
109
+ white-space: nowrap;
110
+ }
111
+ .event-desc {
112
+ font-size: var(--roxy-text-sm, 0.875rem);
113
+ line-height: 1.4;
114
+ }
115
+ .sig {
116
+ grid-column: 2;
117
+ height: 4px;
118
+ border-radius: 2px;
119
+ background: color-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent);
120
+ overflow: hidden;
121
+ }
122
+ .sig-fill {
123
+ display: block;
124
+ height: 100%;
125
+ border-radius: 2px;
126
+ }
127
+ .sig-fill.western {
128
+ background: var(--roxy-accent, #f59e0b);
129
+ }
130
+ .sig-fill.vedic {
131
+ background: var(--roxy-info, #2563eb);
132
+ }
133
+ .sig-fill.biorhythm {
134
+ background: var(--roxy-success, #16a34a);
135
+ }
136
+ .quiet {
137
+ color: var(--roxy-muted, #71717a);
138
+ font-size: var(--roxy-text-sm, 0.875rem);
139
+ font-style: italic;
140
+ }
141
+ `,
142
+ ];
143
+
144
+ protected renderEmpty() {
145
+ return html`<div class="roxy-empty" role="status">No digest data</div>`;
146
+ }
147
+
148
+ protected renderData(d: GenerateDigestResponse) {
149
+ const windows = d.windows ?? [];
150
+ if (windows.length === 0) return this.renderEmpty();
151
+ const range = [formatDate(d.startDate), formatDate(d.endDate)]
152
+ .filter(Boolean)
153
+ .join(' – ');
154
+
155
+ return html`<div class="wrap" aria-label="Forecast digest">
156
+ <div class="head">
157
+ <h2 class="title">Forecast Digest</h2>
158
+ ${range ? html`<p class="subtitle">${range}</p>` : nothing}
159
+ </div>
160
+ ${windows.map((w) => this.renderWindow(w))}
161
+ </div>`;
162
+ }
163
+
164
+ private windowLabel(days: number | undefined): string {
165
+ if (days === 1) return 'Next 24 hours';
166
+ return typeof days === 'number' ? `Next ${days} days` : 'Window';
167
+ }
168
+
169
+ private renderWindow(w: DigestWindow) {
170
+ const top = w.top ?? [];
171
+ const byDomain = w.byDomain ?? {};
172
+ const domains = Object.entries(byDomain) as Array<[string, number]>;
173
+ return html`<section class="window">
174
+ <div class="window-head">
175
+ <span class="window-label">${this.windowLabel(w.days)}</span>
176
+ <span class="window-count">${w.count ?? 0} event${w.count === 1 ? '' : 's'}</span>
177
+ </div>
178
+ ${
179
+ domains.length > 0
180
+ ? html`<div class="domains">
181
+ ${domains.map(
182
+ ([dom, n]) => html`<span class="domain-chip">
183
+ <span class="swatch sw-${dom}"></span>${humanize(dom)} ${n}
184
+ </span>`,
185
+ )}
186
+ </div>`
187
+ : nothing
188
+ }
189
+ ${
190
+ top.length > 0
191
+ ? html`<div role="list">${top.map((e) => this.renderEvent(e))}</div>`
192
+ : html`<p class="quiet">No notable events.</p>`
193
+ }
194
+ </section>`;
195
+ }
196
+
197
+ private renderEvent(e: DigestEvent) {
198
+ const sig = typeof e.significance === 'number' ? e.significance : 0;
199
+ return html`<div class="event" role="listitem">
200
+ <span class="event-date">${formatDate(e.date)}</span>
201
+ <span class="event-desc">${e.description ?? humanize(e.type ?? '')}</span>
202
+ <span class="sig" role="img" aria-label="significance ${formatNumber(sig, 0)} of 100">
203
+ <span class="sig-fill ${e.domain}" style="width:${Math.max(0, Math.min(100, sig))}%"></span>
204
+ </span>
205
+ </div>`;
206
+ }
207
+ }
208
+
209
+ declare global {
210
+ interface HTMLElementTagNameMap {
211
+ 'roxy-forecast-digest': RoxyForecastDigest;
212
+ }
213
+ }
@@ -1,12 +1,22 @@
1
- import { css, html, LitElement, nothing } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ import { css, html, nothing } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
3
  import { ASPECT_SYMBOL } from '../tokens/index.js';
4
- import type { GenerateTimelineResponse } from '../types/index.js';
4
+ import type {
5
+ FindSignificantDatesResponse,
6
+ ForecastTransitsResponse,
7
+ GenerateTimelineResponse,
8
+ } from '../types/index.js';
9
+ import { RoxyDataElement } from '../utils/base-element.js';
5
10
  import { baseStyles } from '../utils/base-styles.js';
6
11
  import { ASPECT_CLASS, formatDate, formatNumber } from '../utils/format.js';
7
- import { MarkupDataController } from '../utils/markup-data.js';
8
12
  import { capitalize, humanize } from '../utils/string.js';
9
13
 
14
+ /** Timeline, significant-dates, and forecast-transits all return the same `{ events, startDate, endDate, birthData, count }` shape. */
15
+ type ForecastTimelineData =
16
+ | GenerateTimelineResponse
17
+ | FindSignificantDatesResponse
18
+ | ForecastTransitsResponse;
19
+
10
20
  type ForecastEvent = GenerateTimelineResponse['events'][number];
11
21
  type ForecastDomain = ForecastEvent['domain'];
12
22
 
@@ -38,11 +48,17 @@ const DOMAIN_ORDER: readonly ForecastDomain[] = [
38
48
  * Theming flows through `--roxy-*` custom properties on `:host`.
39
49
  */
40
50
  @customElement('roxy-forecast-timeline')
41
- export class RoxyForecastTimeline extends LitElement {
51
+ export class RoxyForecastTimeline extends RoxyDataElement<ForecastTimelineData> {
42
52
  static styles = [
43
53
  baseStyles,
44
54
  css`
45
55
  .wrap {
56
+ background: var(--roxy-surface, #fff);
57
+ color: var(--roxy-fg, #0a0a0a);
58
+ border: 1px solid var(--roxy-border, #e4e4e7);
59
+ border-radius: var(--roxy-radius-md, 8px);
60
+ padding: var(--roxy-space-lg, 1.5rem);
61
+ box-shadow: var(--roxy-shadow-sm);
46
62
  display: grid;
47
63
  gap: var(--roxy-space-md, 1rem);
48
64
  }
@@ -195,22 +211,11 @@ export class RoxyForecastTimeline extends LitElement {
195
211
  `,
196
212
  ];
197
213
 
198
- constructor() {
199
- super();
200
- // Enables hydrating `data` from a direct-child
201
- // <script type="application/json" class="roxy-data"> for server-rendered
202
- // and cached consumers. The JavaScript `data` property still wins.
203
- new MarkupDataController(this);
214
+ protected renderEmpty() {
215
+ return html`<div class="roxy-empty" role="status">No forecast data</div>`;
204
216
  }
205
217
 
206
- @property({ attribute: false })
207
- data: GenerateTimelineResponse | null = null;
208
-
209
- render() {
210
- const d = this.data;
211
- if (!d)
212
- return html`<div class="roxy-empty" role="status">No forecast data</div>`;
213
-
218
+ protected renderData(d: ForecastTimelineData) {
214
219
  const events = d.events ?? [];
215
220
  const grouped = this.groupByDate(events);
216
221
  const present = DOMAIN_ORDER.filter((dom) =>
@@ -1,9 +1,9 @@
1
- import { css, html, LitElement, nothing } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ import { css, html, nothing } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
3
  import type { CompatibilityResponse } from '../types/index.js';
4
+ import { RoxyDataElement } from '../utils/base-element.js';
4
5
  import { baseStyles } from '../utils/base-styles.js';
5
6
  import { formatNumber, formatPercent } from '../utils/format.js';
6
- import { MarkupDataController } from '../utils/markup-data.js';
7
7
 
8
8
  const STANDARD_CATEGORIES = [
9
9
  'Varna',
@@ -20,12 +20,12 @@ const STANDARD_CATEGORIES = [
20
20
  * 36-point Ashtakoota score card. Renders /vedic-astrology/compatibility.
21
21
  */
22
22
  @customElement('roxy-guna-milan')
23
- export class RoxyGunaMilan extends LitElement {
23
+ export class RoxyGunaMilan extends RoxyDataElement<CompatibilityResponse> {
24
24
  static styles = [
25
25
  baseStyles,
26
26
  css`
27
27
  .card {
28
- background: var(--roxy-bg, #fff);
28
+ background: var(--roxy-surface, #fff);
29
29
  border: 1px solid var(--roxy-border, #e4e4e7);
30
30
  border-radius: var(--roxy-radius-md, 8px);
31
31
  padding: var(--roxy-space-lg, 1.5rem);
@@ -147,22 +147,11 @@ export class RoxyGunaMilan extends LitElement {
147
147
  `,
148
148
  ];
149
149
 
150
- constructor() {
151
- super();
152
- // Enables hydrating `data` from a direct-child
153
- // <script type="application/json" class="roxy-data"> for server-rendered
154
- // and cached consumers. The JavaScript `data` property still wins.
155
- new MarkupDataController(this);
150
+ protected renderEmpty() {
151
+ return html`<div class="roxy-empty" role="status">No Guna Milan data</div>`;
156
152
  }
157
153
 
158
- @property({ attribute: false })
159
- data: CompatibilityResponse | null = null;
160
-
161
- render() {
162
- const d = this.data;
163
- if (!d)
164
- return html`<div class="roxy-empty" role="status">No Guna Milan data</div>`;
165
-
154
+ protected renderData(d: CompatibilityResponse) {
166
155
  const breakdown = (d.breakdown ?? []).filter(
167
156
  (b) => b?.category !== undefined,
168
157
  );