create-yss-spec 3.5.2 → 3.5.3

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 (438) hide show
  1. package/package.json +1 -1
  2. package/src/template/asset-runtime.js +3 -1
  3. package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
  4. package/template/.agents/skills/codebase-design/SKILL.md +4 -0
  5. package/template/{.codex/skills/improve-codebase-architecture/SKILL.md → .agents/skills/codebase-design/references/architecture-audit.md} +3 -9
  6. package/template/.agents/skills/frontend-commit/SKILL.md +4 -93
  7. package/template/.agents/skills/git-commit-core/SKILL.md +18 -0
  8. package/template/.agents/skills/java-backend-commit/SKILL.md +4 -96
  9. package/template/.agents/skills/llm-wiki/SKILL.md +12 -13
  10. package/template/.agents/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  11. package/template/.agents/skills/llm-wiki/references/compile.md +21 -114
  12. package/template/.agents/skills/llm-wiki/references/ingest.md +8 -33
  13. package/template/.agents/skills/llm-wiki/references/lint.md +9 -46
  14. package/template/.agents/skills/llm-wiki/references/query.md +9 -18
  15. package/template/.agents/skills/llm-wiki/references/schema.md +56 -29
  16. package/template/.agents/skills/llm-wiki/references/transactions.md +48 -0
  17. package/template/.agents/skills/llm-wiki/references/writing.md +4 -4
  18. package/template/.agents/skills/llm-wiki/scripts/advise.mjs +46 -58
  19. package/template/.agents/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  20. package/template/.agents/skills/llm-wiki/scripts/core.mjs +262 -0
  21. package/template/.agents/skills/llm-wiki/scripts/extract.mjs +35 -18
  22. package/template/.agents/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  23. package/template/.agents/skills/llm-wiki/scripts/feedback.mjs +51 -0
  24. package/template/.agents/skills/llm-wiki/scripts/inventory.mjs +43 -171
  25. package/template/.agents/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  26. package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  27. package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  28. package/template/.agents/skills/llm-wiki/scripts/migrate.mjs +51 -0
  29. package/template/.agents/skills/llm-wiki/scripts/query.mjs +149 -0
  30. package/template/.agents/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  31. package/template/.agents/skills/llm-wiki/scripts/sources.mjs +166 -0
  32. package/template/.agents/skills/llm-wiki/scripts/transaction.mjs +384 -0
  33. package/template/.agents/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  34. package/template/.agents/skills/prototype/LOGIC.md +1 -1
  35. package/template/.agents/skills/prototype/SKILL.md +5 -19
  36. package/template/.agents/skills/setup-matt-pocock-skills/SKILL.md +3 -3
  37. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  38. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
  39. package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  40. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +8 -8
  41. package/template/.agents/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  42. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  43. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
  44. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  45. package/template/.agents/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  46. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
  47. package/template/.agents/skills/yss-research/SKILL.md +4 -0
  48. package/template/.codex/skills/codebase-design/SKILL.md +4 -0
  49. package/template/{.pi/skills/improve-codebase-architecture/SKILL.md → .codex/skills/codebase-design/references/architecture-audit.md} +3 -9
  50. package/template/.codex/skills/frontend-commit/SKILL.md +4 -93
  51. package/template/.codex/skills/git-commit-core/SKILL.md +18 -0
  52. package/template/.codex/skills/java-backend-commit/SKILL.md +4 -96
  53. package/template/.codex/skills/llm-wiki/SKILL.md +12 -13
  54. package/template/.codex/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  55. package/template/.codex/skills/llm-wiki/references/compile.md +21 -114
  56. package/template/.codex/skills/llm-wiki/references/ingest.md +8 -33
  57. package/template/.codex/skills/llm-wiki/references/lint.md +9 -46
  58. package/template/.codex/skills/llm-wiki/references/query.md +9 -18
  59. package/template/.codex/skills/llm-wiki/references/schema.md +56 -29
  60. package/template/.codex/skills/llm-wiki/references/transactions.md +48 -0
  61. package/template/.codex/skills/llm-wiki/references/writing.md +4 -4
  62. package/template/.codex/skills/llm-wiki/scripts/advise.mjs +46 -58
  63. package/template/.codex/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  64. package/template/.codex/skills/llm-wiki/scripts/core.mjs +262 -0
  65. package/template/.codex/skills/llm-wiki/scripts/extract.mjs +35 -18
  66. package/template/.codex/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  67. package/template/.codex/skills/llm-wiki/scripts/feedback.mjs +51 -0
  68. package/template/.codex/skills/llm-wiki/scripts/inventory.mjs +43 -171
  69. package/template/.codex/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  70. package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  71. package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  72. package/template/.codex/skills/llm-wiki/scripts/migrate.mjs +51 -0
  73. package/template/.codex/skills/llm-wiki/scripts/query.mjs +149 -0
  74. package/template/.codex/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  75. package/template/.codex/skills/llm-wiki/scripts/sources.mjs +166 -0
  76. package/template/.codex/skills/llm-wiki/scripts/transaction.mjs +384 -0
  77. package/template/.codex/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  78. package/template/.codex/skills/prototype/LOGIC.md +1 -1
  79. package/template/.codex/skills/prototype/SKILL.md +5 -19
  80. package/template/.codex/skills/setup-matt-pocock-skills/SKILL.md +3 -3
  81. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  82. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
  83. package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  84. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +8 -8
  85. package/template/.codex/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  86. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  87. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
  88. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  89. package/template/.codex/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  90. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
  91. package/template/.codex/skills/yss-research/SKILL.md +4 -0
  92. package/template/.cursor/skills/codebase-design/SKILL.md +4 -0
  93. package/template/.cursor/skills/{improve-codebase-architecture/SKILL.md → codebase-design/references/architecture-audit.md} +3 -9
  94. package/template/.cursor/skills/frontend-commit/SKILL.md +4 -93
  95. package/template/.cursor/skills/git-commit-core/SKILL.md +18 -0
  96. package/template/.cursor/skills/java-backend-commit/SKILL.md +4 -96
  97. package/template/.cursor/skills/llm-wiki/SKILL.md +12 -13
  98. package/template/.cursor/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  99. package/template/.cursor/skills/llm-wiki/references/compile.md +21 -114
  100. package/template/.cursor/skills/llm-wiki/references/ingest.md +8 -33
  101. package/template/.cursor/skills/llm-wiki/references/lint.md +9 -46
  102. package/template/.cursor/skills/llm-wiki/references/query.md +9 -18
  103. package/template/.cursor/skills/llm-wiki/references/schema.md +56 -29
  104. package/template/.cursor/skills/llm-wiki/references/transactions.md +48 -0
  105. package/template/.cursor/skills/llm-wiki/references/writing.md +4 -4
  106. package/template/.cursor/skills/llm-wiki/scripts/advise.mjs +46 -58
  107. package/template/.cursor/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  108. package/template/.cursor/skills/llm-wiki/scripts/core.mjs +262 -0
  109. package/template/.cursor/skills/llm-wiki/scripts/extract.mjs +35 -18
  110. package/template/.cursor/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  111. package/template/.cursor/skills/llm-wiki/scripts/feedback.mjs +51 -0
  112. package/template/.cursor/skills/llm-wiki/scripts/inventory.mjs +43 -171
  113. package/template/.cursor/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  114. package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  115. package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  116. package/template/.cursor/skills/llm-wiki/scripts/migrate.mjs +51 -0
  117. package/template/.cursor/skills/llm-wiki/scripts/query.mjs +149 -0
  118. package/template/.cursor/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  119. package/template/.cursor/skills/llm-wiki/scripts/sources.mjs +166 -0
  120. package/template/.cursor/skills/llm-wiki/scripts/transaction.mjs +384 -0
  121. package/template/.cursor/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  122. package/template/.cursor/skills/prototype/LOGIC.md +1 -1
  123. package/template/.cursor/skills/prototype/SKILL.md +5 -19
  124. package/template/.cursor/skills/setup-matt-pocock-skills/SKILL.md +3 -3
  125. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  126. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
  127. package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  128. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +8 -8
  129. package/template/.cursor/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  130. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  131. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
  132. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  133. package/template/.cursor/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  134. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
  135. package/template/.cursor/skills/yss-research/SKILL.md +4 -0
  136. package/template/.pi/skills/codebase-design/SKILL.md +4 -0
  137. package/template/{.agents/skills/improve-codebase-architecture/SKILL.md → .pi/skills/codebase-design/references/architecture-audit.md} +3 -9
  138. package/template/.pi/skills/frontend-commit/SKILL.md +4 -93
  139. package/template/.pi/skills/git-commit-core/SKILL.md +18 -0
  140. package/template/.pi/skills/java-backend-commit/SKILL.md +4 -96
  141. package/template/.pi/skills/llm-wiki/SKILL.md +12 -13
  142. package/template/.pi/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  143. package/template/.pi/skills/llm-wiki/references/compile.md +21 -114
  144. package/template/.pi/skills/llm-wiki/references/ingest.md +8 -33
  145. package/template/.pi/skills/llm-wiki/references/lint.md +9 -46
  146. package/template/.pi/skills/llm-wiki/references/query.md +9 -18
  147. package/template/.pi/skills/llm-wiki/references/schema.md +56 -29
  148. package/template/.pi/skills/llm-wiki/references/transactions.md +48 -0
  149. package/template/.pi/skills/llm-wiki/references/writing.md +4 -4
  150. package/template/.pi/skills/llm-wiki/scripts/advise.mjs +46 -58
  151. package/template/.pi/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  152. package/template/.pi/skills/llm-wiki/scripts/core.mjs +262 -0
  153. package/template/.pi/skills/llm-wiki/scripts/extract.mjs +35 -18
  154. package/template/.pi/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  155. package/template/.pi/skills/llm-wiki/scripts/feedback.mjs +51 -0
  156. package/template/.pi/skills/llm-wiki/scripts/inventory.mjs +43 -171
  157. package/template/.pi/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  158. package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  159. package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  160. package/template/.pi/skills/llm-wiki/scripts/migrate.mjs +51 -0
  161. package/template/.pi/skills/llm-wiki/scripts/query.mjs +149 -0
  162. package/template/.pi/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  163. package/template/.pi/skills/llm-wiki/scripts/sources.mjs +166 -0
  164. package/template/.pi/skills/llm-wiki/scripts/transaction.mjs +384 -0
  165. package/template/.pi/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  166. package/template/.pi/skills/prototype/LOGIC.md +1 -1
  167. package/template/.pi/skills/prototype/SKILL.md +5 -19
  168. package/template/.pi/skills/setup-matt-pocock-skills/SKILL.md +3 -3
  169. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  170. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
  171. package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  172. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +8 -8
  173. package/template/.pi/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  174. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
  175. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
  176. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
  177. package/template/.pi/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  178. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
  179. package/template/.pi/skills/yss-research/SKILL.md +4 -0
  180. package/template/.template-spec/agents/README.md +1 -1
  181. package/template/.template-spec/agents/digital-human-roles.yaml +1 -1
  182. package/template/.template-spec/agents/skill-migrations.md +15 -0
  183. package/template/.template-spec/agents/yss-skill-registry.yaml +10 -29
  184. package/template/.template-spec/architecture/README.md +3 -34
  185. package/template/.template-spec/plan/templates/competitive-analysis-template.md +2 -2
  186. package/template/.template-spec/process/contract-reading.md +24 -0
  187. package/template/.template-spec/process/lifecycle-registry-baseline.json +3 -1
  188. package/template/.template-spec/process/lifecycle-registry.yaml +6 -0
  189. package/template/.template-spec/process/research-completion.md +23 -0
  190. package/template/.template-spec/process/schemas/digital-human-task-package.schema.json +202 -12
  191. package/template/.template-spec/process/schemas/frontend-implementation-evidence.schema.json +7 -15
  192. package/template/.template-spec/process/subagent-collaboration.md +8 -0
  193. package/template/.template-spec/process/templates/frontend-implementation-verification-template.yaml +15 -1
  194. package/template/AGENTS.md +1 -1
  195. package/template/CONTEXT.md +5 -1
  196. package/template/README.md +12 -24
  197. package/template/scripts/contract +5 -5
  198. package/template/scripts/lib/contract-views.mjs +57 -2
  199. package/template/scripts/lib/execution-evidence.mjs +51 -0
  200. package/template/scripts/lib/harness-execution-scope.mjs +1 -1
  201. package/template/scripts/lib/implementation-contract-compiler.mjs +2 -0
  202. package/template/scripts/lib/lifecycle-context-query.mjs +15 -3
  203. package/template/scripts/lib/lifecycle-status.mjs +32 -6
  204. package/template/scripts/lib/lifecycle-transition.mjs +19 -1
  205. package/template/scripts/lib/maintenance-research.mjs +63 -0
  206. package/template/scripts/lib/read-only-intake.mjs +132 -0
  207. package/template/scripts/lib/skill-registry.mjs +2 -1
  208. package/template/scripts/lib/skill-supply-chain.mjs +4 -0
  209. package/template/scripts/lib/slice-task-package.mjs +1 -1
  210. package/template/scripts/lib/task-package.mjs +9 -2
  211. package/template/scripts/lifecycle-status +2 -2
  212. package/template/scripts/prepare-read-only-intake +16 -0
  213. package/template/scripts/run-read-only-intake +14 -0
  214. package/template/scripts/verify-digital-human-task-package +2 -2
  215. package/template/scripts/verify-frontend-implementation-evidence +24 -1
  216. package/template/scripts/verify-maintenance-research +13 -0
  217. package/template/skills-lock.json +19 -76
  218. package/template.manifest.json +118 -29
  219. package/template.snapshot.json +5 -6
  220. package/template/.agents/skills/grill-with-docs/SKILL.md +0 -20
  221. package/template/.agents/skills/grill-with-docs/agents/openai.yaml +0 -5
  222. package/template/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  223. package/template/.agents/skills/prototype/UI.md +0 -112
  224. package/template/.agents/skills/setup-matt-pocock-skills/domain.md +0 -37
  225. package/template/.agents/skills/to-questionnaire/SKILL.md +0 -55
  226. package/template/.agents/skills/to-questionnaire/agents/openai.yaml +0 -5
  227. package/template/.agents/skills/wait-what/SKILL.md +0 -7
  228. package/template/.agents/skills/wait-what/agents/openai.yaml +0 -5
  229. package/template/.codex/skills/data-analytics/.app.json +0 -84
  230. package/template/.codex/skills/data-analytics/.codex-plugin/plugin.json +0 -66
  231. package/template/.codex/skills/data-analytics/.mcp.json +0 -30
  232. package/template/.codex/skills/data-analytics/AGENTS.md +0 -20
  233. package/template/.codex/skills/data-analytics/DEPENDENCIES.MD +0 -27
  234. package/template/.codex/skills/data-analytics/README.md +0 -60
  235. package/template/.codex/skills/data-analytics/__yss_dotfile__.gitignore +0 -2
  236. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html +0 -16
  237. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part001 +0 -1
  238. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part002 +0 -1
  239. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part003 +0 -1
  240. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part004 +0 -1
  241. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part005 +0 -1
  242. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part006 +0 -1
  243. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html +0 -16
  244. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part001 +0 -1
  245. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part002 +0 -1
  246. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part003 +0 -1
  247. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part004 +0 -1
  248. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part005 +0 -1
  249. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part006 +0 -1
  250. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part007 +0 -1
  251. package/template/.codex/skills/data-analytics/assets/datascience-small.svg +0 -5
  252. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html +0 -16
  253. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part001 +0 -1
  254. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part002 +0 -1
  255. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part003 +0 -1
  256. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part004 +0 -1
  257. package/template/.codex/skills/data-analytics/assets/datascience.png +0 -0
  258. package/template/.codex/skills/data-analytics/assets/datascience.svg +0 -10
  259. package/template/.codex/skills/data-analytics/mcp/server.cjs +0 -2964
  260. package/template/.codex/skills/data-analytics/package-lock.json +0 -3048
  261. package/template/.codex/skills/data-analytics/package.json +0 -43
  262. package/template/.codex/skills/data-analytics/scripts/normalize-widget-assets.mjs +0 -75
  263. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +0 -137
  264. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/agents/openai.yaml +0 -6
  265. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/references/quality-checks.md +0 -29
  266. package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +0 -148
  267. package/template/.codex/skills/data-analytics/skills/build-dashboard/agents/openai.yaml +0 -6
  268. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/bi-platform-dashboard.md +0 -18
  269. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/html-dashboard.md +0 -24
  270. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/mcp-artifact-dashboard.md +0 -71
  271. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/streamlit-dashboard.md +0 -85
  272. package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +0 -207
  273. package/template/.codex/skills/data-analytics/skills/build-report/agents/openai.yaml +0 -6
  274. package/template/.codex/skills/data-analytics/skills/build-report/assets/executive-report-shell.html +0 -70
  275. package/template/.codex/skills/data-analytics/skills/build-report/assets/technical-report-shell.html +0 -66
  276. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/SKILL.md +0 -108
  277. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/agents/openai.yaml +0 -6
  278. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/__init__.py +0 -1
  279. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/cli.py +0 -52
  280. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/constants.py +0 -61
  281. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/docx_writer.py +0 -362
  282. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/html_parser.py +0 -829
  283. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/model.py +0 -30
  284. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/plan.py +0 -127
  285. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/quality.py +0 -374
  286. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/rendering.py +0 -613
  287. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/table_utils.py +0 -54
  288. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/utils.py +0 -16
  289. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc_plan.py +0 -9
  290. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/tests/test_delivery_plan.py +0 -45
  291. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/SKILL.md +0 -77
  292. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/agents/openai.yaml +0 -6
  293. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/scripts/report_to_google_slides.py +0 -2379
  294. package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/SKILL.md +0 -88
  295. package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/agents/openai.yaml +0 -6
  296. package/template/.codex/skills/data-analytics/skills/build-report/specifications/executive-report.md +0 -97
  297. package/template/.codex/skills/data-analytics/skills/build-report/specifications/mcp-app-report.md +0 -59
  298. package/template/.codex/skills/data-analytics/skills/build-report/specifications/technical-report.md +0 -75
  299. package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +0 -103
  300. package/template/.codex/skills/data-analytics/skills/design-kpis/agents/openai.yaml +0 -6
  301. package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +0 -68
  302. package/template/.codex/skills/data-analytics/skills/gather-business-context/agents/openai.yaml +0 -6
  303. package/template/.codex/skills/data-analytics/skills/index/SKILL.md +0 -251
  304. package/template/.codex/skills/data-analytics/skills/index/agents/openai.yaml +0 -6
  305. package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +0 -131
  306. package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/agents/openai.yaml +0 -6
  307. package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +0 -141
  308. package/template/.codex/skills/data-analytics/skills/kpi-reporting/agents/openai.yaml +0 -6
  309. package/template/.codex/skills/data-analytics/skills/kpi-reporting/references/report-templates.md +0 -32
  310. package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +0 -106
  311. package/template/.codex/skills/data-analytics/skills/market-sizing/agents/openai.yaml +0 -6
  312. package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +0 -130
  313. package/template/.codex/skills/data-analytics/skills/metric-diagnostics/agents/openai.yaml +0 -6
  314. package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +0 -141
  315. package/template/.codex/skills/data-analytics/skills/product-business-analysis/agents/openai.yaml +0 -6
  316. package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +0 -178
  317. package/template/.codex/skills/data-analytics/skills/spreadsheets/agents/openai.yaml +0 -9
  318. package/template/.codex/skills/data-analytics/skills/spreadsheets/assets/file-spreadsheet.png +0 -0
  319. package/template/.codex/skills/data-analytics/skills/spreadsheets/charts.md +0 -31
  320. package/template/.codex/skills/data-analytics/skills/spreadsheets/references/artifact_tool_api.md +0 -466
  321. package/template/.codex/skills/data-analytics/skills/spreadsheets/style_guidelines.md +0 -99
  322. package/template/.codex/skills/data-analytics/skills/user-context/SKILL.md +0 -197
  323. package/template/.codex/skills/data-analytics/skills/user-context/agents/openai.yaml +0 -6
  324. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/automation-config.md +0 -26
  325. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/source-category-config.json +0 -51
  326. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/user-context-config.md +0 -66
  327. package/template/.codex/skills/data-analytics/skills/user-context/references/automation.md +0 -69
  328. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-examples.md +0 -204
  329. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-state-template.json +0 -87
  330. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding.md +0 -497
  331. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/connector-playbook.md +0 -74
  332. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/setup.md +0 -65
  333. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/skill-template.md +0 -160
  334. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/source-intake.md +0 -75
  335. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/weekly-polling-automation.md +0 -96
  336. package/template/.codex/skills/data-analytics/skills/user-context/references/source-category-runtime.md +0 -263
  337. package/template/.codex/skills/data-analytics/skills/user-context/scripts/data_analytics_preflight.py +0 -1462
  338. package/template/.codex/skills/data-analytics/skills/user-context/scripts/init_user_context_state.py +0 -128
  339. package/template/.codex/skills/data-analytics/skills/user-context/scripts/reset_user_context_state.py +0 -101
  340. package/template/.codex/skills/data-analytics/skills/user-context/scripts/validate_user_context_preflight.py +0 -499
  341. package/template/.codex/skills/data-analytics/skills/user-context/tests/test_state_helpers.py +0 -978
  342. package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +0 -126
  343. package/template/.codex/skills/data-analytics/skills/validate-data/agents/openai.yaml +0 -6
  344. package/template/.codex/skills/data-analytics/skills/validate-data/references/validation-methods.md +0 -70
  345. package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +0 -157
  346. package/template/.codex/skills/data-analytics/skills/visualize-data/agents/openai.yaml +0 -6
  347. package/template/.codex/skills/data-analytics/skills/visualize-data/references/seaborn-templates.md +0 -774
  348. package/template/.codex/skills/data-analytics/src/DESIGN.md +0 -222
  349. package/template/.codex/skills/data-analytics/src/analytics-app/App.tsx +0 -3666
  350. package/template/.codex/skills/data-analytics/src/analytics-app/analytics-layout.test.mjs +0 -136
  351. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartFrame.tsx +0 -54
  352. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartLegend.tsx +0 -96
  353. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartRenderer.tsx +0 -1647
  354. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartTooltip.tsx +0 -245
  355. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-app-helpers.tsx +0 -462
  356. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-capabilities.ts +0 -107
  357. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-compatibility.ts +0 -164
  358. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-contract.ts +0 -192
  359. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-theme.ts +0 -203
  360. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-tokens.css +0 -619
  361. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-transforms.ts +0 -402
  362. package/template/.codex/skills/data-analytics/src/analytics-app/fonts/SystemSansVariableVF.woff2 +0 -0
  363. package/template/.codex/skills/data-analytics/src/analytics-app/imageExport.ts +0 -370
  364. package/template/.codex/skills/data-analytics/src/analytics-app/layout/AnalyticsLayoutCanvas.tsx +0 -536
  365. package/template/.codex/skills/data-analytics/src/analytics-app/layout/RichMarkdown.tsx +0 -681
  366. package/template/.codex/skills/data-analytics/src/analytics-app/layout/analyticsLayoutCore.ts +0 -164
  367. package/template/.codex/skills/data-analytics/src/analytics-app/main.tsx +0 -21
  368. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/chart_contract.py +0 -472
  369. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/check_analytics_app_runtime_links.py +0 -75
  370. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/design_contract.py +0 -142
  371. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/package_utils.py +0 -142
  372. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/tests/test_package_utils.py +0 -377
  373. package/template/.codex/skills/data-analytics/src/analytics-app/styles.css +0 -3468
  374. package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.d.ts +0 -44
  375. package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.jsx +0 -639
  376. package/template/.codex/skills/data-analytics/src/analytics-app/tables/data-table.css +0 -327
  377. package/template/.codex/skills/data-analytics/src/analytics-app/tokens.css +0 -225
  378. package/template/.codex/skills/data-analytics/src/analytics-app/types.ts +0 -204
  379. package/template/.codex/skills/data-analytics/src/analytics-app-core.md +0 -85
  380. package/template/.codex/skills/data-analytics/src/codex-style-contract.md +0 -30
  381. package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.html +0 -12
  382. package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.jsx +0 -1008
  383. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.css +0 -2117
  384. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.html +0 -122
  385. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.js +0 -3588
  386. package/template/.codex/skills/data-analytics/src/datascience-table-widget.css +0 -483
  387. package/template/.codex/skills/data-analytics/src/datascience-table-widget.html +0 -32
  388. package/template/.codex/skills/data-analytics/src/datascience-table-widget.js +0 -333
  389. package/template/.codex/skills/data-analytics/src/mcp-host.js +0 -153
  390. package/template/.codex/skills/data-analytics/src/recharts-config.js +0 -315
  391. package/template/.codex/skills/data-analytics/src/recharts-renderer.jsx +0 -294
  392. package/template/.codex/skills/data-analytics/src/sql-source-view.css +0 -33
  393. package/template/.codex/skills/data-analytics/src/sql-source-view.js +0 -157
  394. package/template/.codex/skills/data-analytics/src/styles/codex-theme.css +0 -121
  395. package/template/.codex/skills/data-analytics/src/table-renderer.jsx +0 -28
  396. package/template/.codex/skills/data-analytics/tests/chart-transforms.test.mjs +0 -87
  397. package/template/.codex/skills/data-analytics/tests/funnel-smoke.html +0 -105
  398. package/template/.codex/skills/data-analytics/tests/inline-widget-compare.html +0 -233
  399. package/template/.codex/skills/data-analytics/tests/mcp-server.test.mjs +0 -1500
  400. package/template/.codex/skills/data-analytics/tests/native-style-contract.test.mjs +0 -219
  401. package/template/.codex/skills/data-analytics/tests/recharts-config.test.mjs +0 -144
  402. package/template/.codex/skills/data-analytics/tests/recharts-renderer.test.mjs +0 -11
  403. package/template/.codex/skills/data-analytics/tests/rich-markdown.test.mjs +0 -54
  404. package/template/.codex/skills/data-analytics/tests/widget-render-harness.html +0 -321
  405. package/template/.codex/skills/data-analytics/tsconfig.json +0 -19
  406. package/template/.codex/skills/data-analytics/vite.config.ts +0 -46
  407. package/template/.codex/skills/grill-with-docs/SKILL.md +0 -20
  408. package/template/.codex/skills/grill-with-docs/agents/openai.yaml +0 -5
  409. package/template/.codex/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  410. package/template/.codex/skills/prototype/UI.md +0 -112
  411. package/template/.codex/skills/setup-matt-pocock-skills/domain.md +0 -37
  412. package/template/.codex/skills/to-questionnaire/SKILL.md +0 -55
  413. package/template/.codex/skills/to-questionnaire/agents/openai.yaml +0 -5
  414. package/template/.codex/skills/wait-what/SKILL.md +0 -7
  415. package/template/.codex/skills/wait-what/agents/openai.yaml +0 -5
  416. package/template/.cursor/skills/grill-with-docs/SKILL.md +0 -20
  417. package/template/.cursor/skills/grill-with-docs/agents/openai.yaml +0 -5
  418. package/template/.cursor/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  419. package/template/.cursor/skills/prototype/UI.md +0 -112
  420. package/template/.cursor/skills/setup-matt-pocock-skills/domain.md +0 -37
  421. package/template/.cursor/skills/to-questionnaire/SKILL.md +0 -55
  422. package/template/.cursor/skills/to-questionnaire/agents/openai.yaml +0 -5
  423. package/template/.cursor/skills/wait-what/SKILL.md +0 -7
  424. package/template/.cursor/skills/wait-what/agents/openai.yaml +0 -5
  425. package/template/.pi/skills/grill-with-docs/SKILL.md +0 -20
  426. package/template/.pi/skills/grill-with-docs/agents/openai.yaml +0 -5
  427. package/template/.pi/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  428. package/template/.pi/skills/prototype/UI.md +0 -112
  429. package/template/.pi/skills/setup-matt-pocock-skills/domain.md +0 -37
  430. package/template/.pi/skills/to-questionnaire/SKILL.md +0 -55
  431. package/template/.pi/skills/to-questionnaire/agents/openai.yaml +0 -5
  432. package/template/.pi/skills/wait-what/SKILL.md +0 -7
  433. package/template/.pi/skills/wait-what/agents/openai.yaml +0 -5
  434. package/template/.template-spec/agents/domain.md +0 -31
  435. /package/template/.agents/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
  436. /package/template/.codex/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
  437. /package/template/.cursor/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
  438. /package/template/.pi/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
@@ -1,1647 +0,0 @@
1
- import { Fragment, type CSSProperties, type ReactNode, type SVGProps } from "react";
2
- import {
3
- Area,
4
- AreaChart,
5
- Bar,
6
- BarChart,
7
- CartesianGrid,
8
- Cell,
9
- Funnel,
10
- FunnelChart,
11
- LabelList,
12
- Line,
13
- LineChart,
14
- Pie,
15
- PieChart,
16
- ReferenceLine,
17
- ResponsiveContainer,
18
- Scatter,
19
- ScatterChart,
20
- Tooltip,
21
- XAxis,
22
- YAxis,
23
- ZAxis,
24
- } from "recharts";
25
-
26
- import type { ChartSeriesSpec, ChartSpec, ChartSurface, ChartValueLabelMode } from "./chart-contract";
27
- import { ChartFrame } from "./ChartFrame";
28
- import { BoxPlotTooltip, ChartTooltip, type ChartTooltipPayloadItem, WaterfallTooltip } from "./ChartTooltip";
29
- import {
30
- asFiniteNumber,
31
- asNumber,
32
- buildBoxPlotRows,
33
- buildFunnelRows,
34
- buildPieRows,
35
- buildWaterfallRows,
36
- chartEmptyMessage,
37
- clamp,
38
- formatDateAxisLabel,
39
- formatValue,
40
- getBoxPlotXAxisScale,
41
- getDateAxisTicks,
42
- getYAxisScaleForSeries,
43
- type ChartDataRow,
44
- type WaterfallDataRow,
45
- } from "./chart-transforms";
46
- import {
47
- colorForSeries,
48
- FUNNEL_STAGE_COLORS,
49
- HEATMAP_BLUE_SCALE,
50
- isHorizontalChartType,
51
- isNormalizedStackedChartType,
52
- isStackedChartType,
53
- } from "./chart-theme";
54
- import { chartEncodingField, rechartsChartFromEncodedSpec } from "./chart-app-helpers";
55
-
56
- const AXIS_LABEL_GAP = 8;
57
- const RECHARTS_AXIS_TICK_SIZE = 6;
58
- const RECHARTS_AXIS_TICK_MARGIN = AXIS_LABEL_GAP - RECHARTS_AXIS_TICK_SIZE;
59
- const Y_AXIS_TICK_LABEL_OVERHANG = 8;
60
- const CHART_COMPACT_HEIGHT = 240;
61
- const CHART_CARD_DEFAULT_HEIGHT = 320;
62
- const CHART_CARD_ROTATED_CATEGORY_HEIGHT = 400;
63
- const CHART_FULLSCREEN_HEIGHT = 520;
64
- const CHART_STATIC_WIDTH = 760;
65
- const CHART_MIN_PLOT_HEIGHT = 180;
66
- const CHART_LEGEND_RESERVED_HEIGHT = 28;
67
- const CHART_HORIZONTAL_BAR_ROW_HEIGHT = 28;
68
- const COMPLEX_CHART_GRID_GAP = 4;
69
- const COMPLEX_CHART_MIN_ROW_HEIGHT = 28;
70
- const HEATMAP_HEADER_ROW_HEIGHT = 24;
71
- const FUNNEL_CORNER_RADIUS = 7;
72
- const FUNNEL_LABEL_GAP = 6;
73
- const FUNNEL_SEGMENT_GAP = 4;
74
- const SCATTER_AUTO_LABEL_LIMIT = 20;
75
- const CATEGORY_X_AXIS_LABEL_ROTATION_DEGREES = -40;
76
- const CATEGORY_X_AXIS_LONG_LABEL_LENGTH = 16;
77
- const CATEGORY_X_AXIS_LONG_AVERAGE_LABEL_LENGTH = 11;
78
- const CATEGORY_X_AXIS_ROTATED_HEIGHT = 82;
79
- const CATEGORY_X_AXIS_ROTATED_HEIGHT_WITH_TITLE = 96;
80
- const CATEGORY_X_AXIS_ROTATED_TICK_MARGIN = 10;
81
- const CATEGORY_X_AXIS_WRAPPED_HEIGHT = 38;
82
- const CATEGORY_X_AXIS_WRAPPED_HEIGHT_WITH_TITLE = 48;
83
- const CATEGORY_X_AXIS_WRAPPED_TICK_MARGIN = 6;
84
- const CATEGORY_X_AXIS_MIN_LINE_CHARACTERS = 8;
85
- const CATEGORY_X_AXIS_ESTIMATED_CHAR_WIDTH = 7;
86
- const CATEGORY_X_AXIS_LABEL_SLOT_GAP = 16;
87
- const BAR_AUTO_VALUE_LABEL_LIMIT = 8;
88
- const BAR_VALUE_LABEL_OFFSET = 8;
89
- const BAR_NEGATIVE_VALUE_LABEL_OFFSET = 18;
90
- const BAR_VALUE_LABEL_HORIZONTAL_GUTTER = 56;
91
- const BAR_VALUE_LABEL_VERTICAL_GUTTER = 34;
92
- const BAR_NEGATIVE_VALUE_LABEL_AXIS_GUTTER = 40;
93
-
94
- type AxisTickCoordinate = number | string;
95
-
96
- type AxisTickProps = {
97
- payload?: { value?: unknown };
98
- textAnchor?: "end" | "inherit" | "middle" | "start";
99
- x?: AxisTickCoordinate;
100
- y?: AxisTickCoordinate;
101
- };
102
-
103
- type WaterfallBarShapeProps = {
104
- height?: number | string;
105
- isActive?: boolean;
106
- parentViewBox?: { width?: number };
107
- payload?: WaterfallDataRow;
108
- width?: number | string;
109
- x?: number | string;
110
- y?: number | string;
111
- };
112
-
113
- type SvgPoint = {
114
- x: number;
115
- y: number;
116
- };
117
-
118
- type FunnelShapeProps = {
119
- className?: string;
120
- fill?: string;
121
- height?: number | string;
122
- labelViewBox?: FunnelViewBox;
123
- lowerWidth?: number | string;
124
- name?: unknown;
125
- option?: unknown;
126
- parentViewBox?: unknown;
127
- payload?: ChartDataRow;
128
- shapeType?: unknown;
129
- stroke?: string;
130
- strokeWidth?: number | string;
131
- tooltipPayload?: unknown;
132
- tooltipPosition?: unknown;
133
- upperWidth?: number | string;
134
- val?: unknown;
135
- value?: unknown;
136
- width?: number | string;
137
- x?: number | string;
138
- y?: number | string;
139
- };
140
-
141
- type FunnelViewBox = {
142
- height?: number | string;
143
- lowerWidth?: number | string;
144
- upperWidth?: number | string;
145
- width?: number | string;
146
- x?: number | string;
147
- y?: number | string;
148
- };
149
-
150
- type FunnelLabelProps = {
151
- height?: number | string;
152
- payload?: ChartDataRow;
153
- value?: unknown;
154
- viewBox?: {
155
- height?: number | string;
156
- width?: number | string;
157
- x?: number | string;
158
- y?: number | string;
159
- };
160
- width?: number | string;
161
- x?: number | string;
162
- y?: number | string;
163
- };
164
-
165
- type BarValueLabelProps = {
166
- height?: number | string;
167
- value?: unknown;
168
- width?: number | string;
169
- x?: number | string;
170
- y?: number | string;
171
- };
172
-
173
- type BarValueLabelSides = {
174
- hasNegative: boolean;
175
- hasNonNegative: boolean;
176
- };
177
-
178
- type BarSignState = {
179
- hasNegative: boolean;
180
- hasPositive: boolean;
181
- hasZero: boolean;
182
- };
183
-
184
- function chartHeightForSurface(surface: ChartSurface, height?: number | string): number | string | undefined {
185
- if (height != null) return height;
186
- if (surface === "explorer") return CHART_FULLSCREEN_HEIGHT;
187
- return undefined;
188
- }
189
-
190
- function chartCardHeightForContent({
191
- hasLegend,
192
- horizontal,
193
- rotateCategoryXAxisLabels,
194
- rowCount,
195
- }: {
196
- hasLegend: boolean;
197
- horizontal: boolean;
198
- rotateCategoryXAxisLabels: boolean;
199
- rowCount: number;
200
- }): number {
201
- const legendReserve = hasLegend ? CHART_LEGEND_RESERVED_HEIGHT : 0;
202
-
203
- if (horizontal) {
204
- return Math.max(
205
- CHART_CARD_DEFAULT_HEIGHT,
206
- Math.max(1, rowCount) * CHART_HORIZONTAL_BAR_ROW_HEIGHT + legendReserve + 96,
207
- );
208
- }
209
-
210
- if (rotateCategoryXAxisLabels) {
211
- return CHART_CARD_ROTATED_CATEGORY_HEIGHT + legendReserve;
212
- }
213
-
214
- return CHART_CARD_DEFAULT_HEIGHT + legendReserve;
215
- }
216
-
217
- function chartFrameHeightForContent({
218
- hasLegend,
219
- height,
220
- horizontal,
221
- rotateCategoryXAxisLabels,
222
- rowCount,
223
- surface,
224
- }: {
225
- hasLegend: boolean;
226
- height?: number | string;
227
- horizontal: boolean;
228
- rotateCategoryXAxisLabels: boolean;
229
- rowCount: number;
230
- surface: ChartSurface;
231
- }): number | string | undefined {
232
- const requestedHeight = chartHeightForSurface(surface, height);
233
- if (requestedHeight != null) return requestedHeight;
234
- if (surface !== "card") return requestedHeight;
235
-
236
- return chartCardHeightForContent({
237
- hasLegend,
238
- horizontal,
239
- rotateCategoryXAxisLabels,
240
- rowCount,
241
- });
242
- }
243
-
244
- function numericChartDimension(value: number | string | undefined, fallback: number): number {
245
- if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
246
- if (typeof value === "string") {
247
- const parsed = Number(value);
248
- if (Number.isFinite(parsed) && parsed > 0) return parsed;
249
- }
250
- return fallback;
251
- }
252
-
253
- function complexChartFrameHeight({
254
- height,
255
- rowCount,
256
- surface,
257
- headerRows = 0,
258
- }: {
259
- height?: number | string;
260
- rowCount: number;
261
- surface: ChartSurface;
262
- headerRows?: number;
263
- }): number | string | undefined {
264
- const requestedHeight = chartHeightForSurface(surface, height);
265
- if (typeof requestedHeight === "string" && Number.isNaN(Number(requestedHeight))) {
266
- return requestedHeight;
267
- }
268
- const defaultHeight = surface === "explorer" ? CHART_FULLSCREEN_HEIGHT : CHART_COMPACT_HEIGHT;
269
- const baseHeight = numericChartDimension(requestedHeight, defaultHeight);
270
- const visibleRows = Math.max(0, rowCount);
271
- const visibleHeaderRows = Math.max(0, headerRows);
272
- const rowSlots = visibleRows + visibleHeaderRows;
273
- const minimumContentHeight =
274
- visibleHeaderRows * HEATMAP_HEADER_ROW_HEIGHT +
275
- visibleRows * COMPLEX_CHART_MIN_ROW_HEIGHT +
276
- Math.max(0, rowSlots - 1) * COMPLEX_CHART_GRID_GAP;
277
-
278
- return Math.max(baseHeight, minimumContentHeight);
279
- }
280
-
281
- function NumericYAxisTick({
282
- payload,
283
- textAnchor = "end",
284
- valueFormat = "compact",
285
- x = 0,
286
- y = 0,
287
- }: AxisTickProps & { valueFormat?: ChartSpec["valueFormat"] }) {
288
- const numericX = typeof x === "number" ? x : Number(x) || 0;
289
- const numericY = typeof y === "number" ? y : Number(y) || 0;
290
- return (
291
- <text
292
- className="recharts-cartesian-axis-tick-value chart-axis-number"
293
- dominantBaseline="middle"
294
- textAnchor={textAnchor}
295
- x={numericX}
296
- y={numericY}
297
- >
298
- {formatValue(payload?.value, valueFormat)}
299
- </text>
300
- );
301
- }
302
-
303
- function XAxisEndpointTick({
304
- firstTick,
305
- lastTick,
306
- payload,
307
- x = 0,
308
- y = 0,
309
- }: AxisTickProps & { firstTick?: string; lastTick?: string }) {
310
- const value = String(payload?.value ?? "");
311
- const numericX = typeof x === "number" ? x : Number(x) || 0;
312
- const textAnchor = value === firstTick ? "start" : value === lastTick ? "end" : "middle";
313
- return (
314
- <text
315
- className="recharts-cartesian-axis-tick-value"
316
- dominantBaseline="hanging"
317
- textAnchor={textAnchor}
318
- x={numericX}
319
- y={y}
320
- >
321
- {formatDateAxisLabel(value)}
322
- </text>
323
- );
324
- }
325
-
326
- function truncateCategoryAxisLabel(value: string, maxLength: number): string {
327
- if (value.length <= maxLength) return value;
328
- const contentLength = Math.max(1, maxLength - 3);
329
- return `${value.slice(0, contentLength).trimEnd()}...`;
330
- }
331
-
332
- function wrapCategoryAxisLabel(value: string, maxLength: number): string[] {
333
- const normalized = value.trim();
334
- if (!normalized || normalized.length <= maxLength) return [normalized];
335
- const words = normalized.split(/\s+/);
336
- if (words.length === 1) {
337
- return [
338
- normalized.slice(0, maxLength),
339
- truncateCategoryAxisLabel(normalized.slice(maxLength), maxLength),
340
- ].filter(Boolean);
341
- }
342
-
343
- let firstLine = words.shift() ?? "";
344
- while (words.length && `${firstLine} ${words[0]}`.length <= maxLength) {
345
- firstLine = `${firstLine} ${words.shift()}`;
346
- }
347
- return [
348
- truncateCategoryAxisLabel(firstLine, maxLength),
349
- truncateCategoryAxisLabel(words.join(" "), maxLength),
350
- ].filter(Boolean);
351
- }
352
-
353
- function WrappedCategoryXAxisTick({
354
- maxLineLength,
355
- payload,
356
- x = 0,
357
- y = 0,
358
- }: AxisTickProps & { maxLineLength: number }) {
359
- const numericX = typeof x === "number" ? x : Number(x) || 0;
360
- const lines = wrapCategoryAxisLabel(String(payload?.value ?? ""), maxLineLength);
361
- return (
362
- <text
363
- className="recharts-cartesian-axis-tick-value"
364
- dominantBaseline="hanging"
365
- textAnchor="middle"
366
- x={numericX}
367
- y={y}
368
- >
369
- {lines.map((line, index) => (
370
- <tspan dy={index === 0 ? 0 : "1.2em"} key={`${line}-${index}`} x={numericX}>
371
- {line}
372
- </tspan>
373
- ))}
374
- </text>
375
- );
376
- }
377
-
378
- function categoryXAxisLabels(chart: ChartSpec, rows: ChartDataRow[]): string[] {
379
- return [
380
- ...new Set(
381
- rows
382
- .map((row) => row[chart.xField])
383
- .filter((value) => value != null)
384
- .map((value) => String(value)),
385
- ),
386
- ];
387
- }
388
-
389
- function categoryXAxisLabelSlotWidth(labels: string[], availableWidth: number): number {
390
- return Math.max(
391
- 0,
392
- availableWidth / Math.max(1, labels.length) - CATEGORY_X_AXIS_LABEL_SLOT_GAP,
393
- );
394
- }
395
-
396
- function shouldRotateCategoryXAxisLabels({
397
- chart,
398
- horizontal,
399
- xAxisTicks,
400
- }: {
401
- chart: ChartSpec;
402
- horizontal: boolean;
403
- xAxisTicks?: string[];
404
- }): boolean {
405
- if (horizontal || xAxisTicks?.length) return false;
406
- return chart.settings?.categoryLabelPolicy === "rotate";
407
- }
408
-
409
- function shouldWrapCategoryXAxisLabels({
410
- availableWidth,
411
- chart,
412
- horizontal,
413
- rows,
414
- xAxisTicks,
415
- }: {
416
- availableWidth: number;
417
- chart: ChartSpec;
418
- horizontal: boolean;
419
- rows: ChartDataRow[];
420
- xAxisTicks?: string[];
421
- }): boolean {
422
- if (horizontal || xAxisTicks?.length) return false;
423
- const labelPolicy = chart.settings?.categoryLabelPolicy;
424
- if (labelPolicy === "wrap") return true;
425
- if (labelPolicy === "rotate" || labelPolicy === "truncate") return false;
426
-
427
- const labels = categoryXAxisLabels(chart, rows);
428
- if (labels.length < 2) return false;
429
-
430
- const maxLength = Math.max(...labels.map((label) => label.length));
431
- const longestLabelWidth = maxLength * CATEGORY_X_AXIS_ESTIMATED_CHAR_WIDTH;
432
- const labelSlotWidth = categoryXAxisLabelSlotWidth(labels, availableWidth);
433
- if (longestLabelWidth <= labelSlotWidth) return false;
434
-
435
- if (maxLength >= CATEGORY_X_AXIS_LONG_LABEL_LENGTH) return true;
436
-
437
- const averageLength =
438
- labels.reduce((total, label) => total + label.length, 0) / labels.length;
439
- return labels.length >= 4 && averageLength >= CATEGORY_X_AXIS_LONG_AVERAGE_LABEL_LENGTH;
440
- }
441
-
442
- function categoryXAxisWrappedLineLength(chart: ChartSpec, rows: ChartDataRow[], availableWidth: number): number {
443
- const slotWidth = categoryXAxisLabelSlotWidth(categoryXAxisLabels(chart, rows), availableWidth);
444
- return Math.max(CATEGORY_X_AXIS_MIN_LINE_CHARACTERS, Math.floor(slotWidth / CATEGORY_X_AXIS_ESTIMATED_CHAR_WIDTH));
445
- }
446
-
447
- function getFunnelStageColor(index: number, rowCount: number): string {
448
- const colorIndex = Math.round(
449
- (index * (FUNNEL_STAGE_COLORS.length - 1)) / Math.max(1, rowCount - 1),
450
- );
451
- return FUNNEL_STAGE_COLORS[clamp(colorIndex, 0, FUNNEL_STAGE_COLORS.length - 1)];
452
- }
453
-
454
- function getHeatmapFill(intensity: number): string {
455
- const index = clamp(
456
- Math.floor(intensity * HEATMAP_BLUE_SCALE.length),
457
- 0,
458
- HEATMAP_BLUE_SCALE.length - 1,
459
- );
460
- return HEATMAP_BLUE_SCALE[index] ?? HEATMAP_BLUE_SCALE[0];
461
- }
462
-
463
- function formatSvgNumber(value: number): string {
464
- return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/\.?0+$/, "");
465
- }
466
-
467
- function getPointDistance(from: SvgPoint, to: SvgPoint): number {
468
- return Math.hypot(to.x - from.x, to.y - from.y);
469
- }
470
-
471
- function movePointToward(from: SvgPoint, to: SvgPoint, distance: number): SvgPoint {
472
- const totalDistance = getPointDistance(from, to);
473
- if (!Number.isFinite(totalDistance) || totalDistance <= 0 || distance <= 0) return from;
474
- const ratio = Math.min(1, distance / totalDistance);
475
- return {
476
- x: from.x + (to.x - from.x) * ratio,
477
- y: from.y + (to.y - from.y) * ratio,
478
- };
479
- }
480
-
481
- function interpolatePoint(from: SvgPoint, to: SvgPoint, ratio: number): SvgPoint {
482
- return {
483
- x: from.x + (to.x - from.x) * ratio,
484
- y: from.y + (to.y - from.y) * ratio,
485
- };
486
- }
487
-
488
- function getRoundedPolygonPath(points: SvgPoint[], radius: number): string {
489
- if (points.length < 3) return "";
490
- const cornerRadii = points.map((point, index) => {
491
- const previous = points[(index - 1 + points.length) % points.length] ?? point;
492
- const next = points[(index + 1) % points.length] ?? point;
493
- return Math.min(radius, getPointDistance(point, previous) / 2, getPointDistance(point, next) / 2);
494
- });
495
- const cornerStarts = points.map((point, index) => {
496
- const previous = points[(index - 1 + points.length) % points.length] ?? point;
497
- return movePointToward(point, previous, cornerRadii[index] ?? 0);
498
- });
499
- const cornerEnds = points.map((point, index) => {
500
- const next = points[(index + 1) % points.length] ?? point;
501
- return movePointToward(point, next, cornerRadii[index] ?? 0);
502
- });
503
- const firstStart = cornerStarts[0];
504
- if (!firstStart) return "";
505
- const commands = [`M ${formatSvgNumber(firstStart.x)} ${formatSvgNumber(firstStart.y)}`];
506
- points.forEach((point, index) => {
507
- const cornerEnd = cornerEnds[index];
508
- if (!cornerEnd) return;
509
- commands.push(
510
- `Q ${formatSvgNumber(point.x)} ${formatSvgNumber(point.y)} ${formatSvgNumber(cornerEnd.x)} ${formatSvgNumber(cornerEnd.y)}`,
511
- );
512
- const nextStart = cornerStarts[index + 1];
513
- if (nextStart) {
514
- commands.push(`L ${formatSvgNumber(nextStart.x)} ${formatSvgNumber(nextStart.y)}`);
515
- }
516
- });
517
- commands.push("Z");
518
- return commands.join(" ");
519
- }
520
-
521
- const PLANNING_SERIES_ROLES = new Set(["baseline", "target", "forecast", "plan"]);
522
-
523
- function isTrendChartType(type: ChartSpec["type"]): boolean {
524
- return type === "area" || type === "line" || type === "sparkline" || type === "stackedArea";
525
- }
526
-
527
- function isPlanningSeries(series: ChartSeriesSpec): boolean {
528
- return PLANNING_SERIES_ROLES.has(series.semanticRole ?? series.role ?? "");
529
- }
530
-
531
- function getSeriesColor(chart: ChartSpec, series: ChartSeriesSpec, index: number): string {
532
- const color = isPlanningSeries(series) && !series.color
533
- ? "neutral"
534
- : series.color;
535
- return colorForSeries(color, index, {
536
- singleSeriesTrend: (chart.type === "line" || chart.type === "area") && chart.series.length === 1,
537
- stacked: isStackedChartType(chart.type, chart.settings?.groupMode),
538
- });
539
- }
540
-
541
- function getSeriesStrokeDasharray(chart: ChartSpec, series: ChartSeriesSpec): string | undefined {
542
- if (!isTrendChartType(chart.type)) return undefined;
543
- if (series.lineStyle === "solid") return undefined;
544
- if (series.lineStyle === "dotted") return "2 4";
545
- if (series.lineStyle === "dashed" || isPlanningSeries(series)) return "5 5";
546
- return undefined;
547
- }
548
-
549
- function shouldColorBarsByCategory(): boolean {
550
- return false;
551
- }
552
-
553
- function renderCategoryBarCells(chart: ChartSpec, series: ChartSeriesSpec, rows: ChartDataRow[], colorBarsBySign = false) {
554
- if (colorBarsBySign) {
555
- return rows.map((row, index) => (
556
- <Cell
557
- fill={barSignColor(asNumber(row[series.field]))}
558
- key={`${chart.id}-${series.field}-${String(row[chart.xField] ?? index)}-${index}`}
559
- />
560
- ));
561
- }
562
- if (!shouldColorBarsByCategory() || series.color) return null;
563
- return rows.map((row, index) => (
564
- <Cell
565
- fill={colorForSeries(undefined, index)}
566
- key={`${chart.id}-${series.field}-${String(row[chart.xField] ?? index)}-${index}`}
567
- />
568
- ));
569
- }
570
-
571
- function waterfallBarPath({
572
- height,
573
- isPositive,
574
- radius,
575
- width,
576
- x,
577
- y,
578
- }: {
579
- height: number;
580
- isPositive: boolean;
581
- radius: number;
582
- width: number;
583
- x: number;
584
- y: number;
585
- }) {
586
- const right = x + width;
587
- const bottom = y + height;
588
-
589
- if (radius <= 0) {
590
- return `M${x},${y}H${right}V${bottom}H${x}Z`;
591
- }
592
-
593
- if (isPositive) {
594
- return [
595
- `M${x},${bottom}`,
596
- `V${y + radius}`,
597
- `Q${x},${y} ${x + radius},${y}`,
598
- `H${right - radius}`,
599
- `Q${right},${y} ${right},${y + radius}`,
600
- `V${bottom}`,
601
- "Z",
602
- ].join("");
603
- }
604
-
605
- return [
606
- `M${x},${y}`,
607
- `H${right}`,
608
- `V${bottom - radius}`,
609
- `Q${right},${bottom} ${right - radius},${bottom}`,
610
- `H${x + radius}`,
611
- `Q${x},${bottom} ${x},${bottom - radius}`,
612
- "Z",
613
- ].join("");
614
- }
615
-
616
- function renderWaterfallBarShape(props: WaterfallBarShapeProps) {
617
- const payload = props.payload;
618
- const x = asFiniteNumber(props.x);
619
- const y = asFiniteNumber(props.y);
620
- const width = Math.max(2, asFiniteNumber(props.width));
621
- const height = Math.max(2, asFiniteNumber(props.height));
622
- const isPositive = Boolean(payload?.__waterfallIsPositive);
623
- const isLast = Boolean(payload?.__waterfallIsLast);
624
- const value = asFiniteNumber(payload?.__waterfallValue);
625
- const rowCount = Math.max(1, asFiniteNumber(payload?.__waterfallRowCount, 1));
626
- const slotWidth = props.parentViewBox?.width ? props.parentViewBox.width / rowCount : width;
627
- const connectorY = isPositive ? y : y + height;
628
- const connectorEndX = x + slotWidth;
629
- const radius = value === 0 ? 0 : Math.min(6, width / 2, height / 2);
630
- const fill = isPositive ? "var(--ds-chart-series-green)" : "var(--ds-chart-series-red)";
631
-
632
- return (
633
- <g className={`waterfall-bar-shape ${props.isActive ? "active" : ""}`}>
634
- {!isLast ? (
635
- <line
636
- className="waterfall-connector"
637
- stroke="var(--ds-chart-reference-line)"
638
- strokeLinecap="round"
639
- strokeWidth={1}
640
- vectorEffect="non-scaling-stroke"
641
- x1={x + width + 3}
642
- x2={Math.max(x + width + 3, connectorEndX - 3)}
643
- y1={connectorY}
644
- y2={connectorY}
645
- />
646
- ) : null}
647
- <path
648
- d={waterfallBarPath({ height, isPositive, radius, width, x, y })}
649
- fill={fill}
650
- vectorEffect="non-scaling-stroke"
651
- />
652
- </g>
653
- );
654
- }
655
-
656
- function renderRoundedFunnelShape(props: FunnelShapeProps) {
657
- const {
658
- className,
659
- fill,
660
- height,
661
- labelViewBox,
662
- lowerWidth,
663
- name: _name,
664
- option: _option,
665
- parentViewBox: _parentViewBox,
666
- payload,
667
- shapeType: _shapeType,
668
- stroke: _stroke,
669
- strokeWidth: _strokeWidth,
670
- tooltipPayload: _tooltipPayload,
671
- tooltipPosition: _tooltipPosition,
672
- upperWidth,
673
- val: _val,
674
- value: _value,
675
- width: _width,
676
- x,
677
- y,
678
- ...svgProps
679
- } = props;
680
- const topX = asFiniteNumber(labelViewBox?.x ?? x);
681
- const topY = asFiniteNumber(labelViewBox?.y ?? y);
682
- const topWidth = asFiniteNumber(labelViewBox?.upperWidth ?? upperWidth);
683
- const bottomWidth = asFiniteNumber(labelViewBox?.lowerWidth ?? lowerWidth);
684
- const segmentHeight = asFiniteNumber(labelViewBox?.height ?? height);
685
- if (topWidth <= 0 || bottomWidth <= 0 || segmentHeight <= 0) return null;
686
-
687
- const segmentIndex = asFiniteNumber(payload?.__funnelIndex);
688
- const segmentCount = asFiniteNumber(payload?.__funnelRowCount, 1);
689
- const topInset = segmentIndex > 0 ? FUNNEL_SEGMENT_GAP / 2 : 0;
690
- const bottomInset = segmentIndex < segmentCount - 1 ? FUNNEL_SEGMENT_GAP / 2 : 0;
691
- const topInsetRatio = topInset / segmentHeight;
692
- const bottomInsetRatio = bottomInset / segmentHeight;
693
- const widthGap = topWidth - bottomWidth;
694
- const bottomRightX = topX + topWidth - widthGap / 2;
695
- const bottomLeftX = bottomRightX - bottomWidth;
696
- const topLeft = { x: topX, y: topY };
697
- const topRight = { x: topX + topWidth, y: topY };
698
- const bottomRight = { x: bottomRightX, y: topY + segmentHeight };
699
- const bottomLeft = { x: bottomLeftX, y: topY + segmentHeight };
700
- const segmentPath = getRoundedPolygonPath(
701
- [
702
- interpolatePoint(topLeft, bottomLeft, topInsetRatio),
703
- interpolatePoint(topRight, bottomRight, topInsetRatio),
704
- interpolatePoint(bottomRight, topRight, bottomInsetRatio),
705
- interpolatePoint(bottomLeft, topLeft, bottomInsetRatio),
706
- ],
707
- FUNNEL_CORNER_RADIUS,
708
- );
709
-
710
- return (
711
- <path
712
- {...(svgProps as SVGProps<SVGPathElement>)}
713
- className={`funnel-segment ${className ?? ""}`.trim()}
714
- d={segmentPath}
715
- fill={fill}
716
- shapeRendering="geometricPrecision"
717
- stroke="none"
718
- />
719
- );
720
- }
721
-
722
- function renderFunnelCenterLabel(props: FunnelLabelProps) {
723
- const viewBox = props.viewBox;
724
- const x = asFiniteNumber(viewBox?.x ?? props.x, Number.NaN);
725
- const y = asFiniteNumber(viewBox?.y ?? props.y, Number.NaN);
726
- const width = asFiniteNumber(viewBox?.width ?? props.width, Number.NaN);
727
- const height = asFiniteNumber(viewBox?.height ?? props.height, Number.NaN);
728
- if (![x, y, width, height].every(Number.isFinite)) return null;
729
-
730
- const [fallbackLabel = "", fallbackValue = ""] =
731
- typeof props.value === "string" ? props.value.split("\u0000") : [];
732
- const label = String(props.payload?.__funnelLabel ?? fallbackLabel);
733
- const value = String(props.payload?.__funnelValueLabel ?? fallbackValue);
734
- if (!label && !value) return null;
735
-
736
- return (
737
- <text className="funnel-label" dominantBaseline="middle" textAnchor="middle" x={x + width / 2} y={y + height / 2}>
738
- <tspan className="funnel-label-text">{label}</tspan>
739
- <tspan className="funnel-label-value" dx={FUNNEL_LABEL_GAP}>
740
- {value}
741
- </tspan>
742
- </text>
743
- );
744
- }
745
-
746
- function renderScatterPointLabel(props: {
747
- value?: unknown;
748
- x?: number | string;
749
- y?: number | string;
750
- }) {
751
- const label = props.value == null ? "" : String(props.value).trim();
752
- const x = asFiniteNumber(props.x, Number.NaN);
753
- const y = asFiniteNumber(props.y, Number.NaN);
754
- if (!label || !Number.isFinite(x) || !Number.isFinite(y)) return null;
755
- const shortLabel = label.length > 28 ? `${label.slice(0, 25)}...` : label;
756
- return (
757
- <text className="scatter-point-label" dominantBaseline="auto" textAnchor="middle" x={x} y={y - 8}>
758
- {shortLabel}
759
- </text>
760
- );
761
- }
762
-
763
- function valueLabelMode(chart: ChartSpec): ChartValueLabelMode {
764
- const explicitMode = chart.labels?.values;
765
- if (explicitMode) return explicitMode;
766
- if (chart.settings?.showValues === true) return "all";
767
- if (chart.settings?.showValues === false) return "none";
768
- return "auto";
769
- }
770
-
771
- function visibleBarValueCount(rows: ChartDataRow[], series: ChartSeriesSpec[]): number {
772
- return rows.reduce(
773
- (count, row) => count + series.filter((candidate) => asNumber(row[candidate.field]) != null).length,
774
- 0,
775
- );
776
- }
777
-
778
- function visibleBarValueLabelSides(rows: ChartDataRow[], series: ChartSeriesSpec[]): BarValueLabelSides {
779
- return rows.reduce<BarValueLabelSides>(
780
- (sides, row) => {
781
- for (const candidate of series) {
782
- const value = asNumber(row[candidate.field]);
783
- if (value == null) continue;
784
- if (value < 0) {
785
- sides.hasNegative = true;
786
- } else {
787
- sides.hasNonNegative = true;
788
- }
789
- }
790
- return sides;
791
- },
792
- { hasNegative: false, hasNonNegative: false },
793
- );
794
- }
795
-
796
- function barSignState(rows: ChartDataRow[], series: ChartSeriesSpec[]): BarSignState {
797
- return rows.reduce<BarSignState>(
798
- (state, row) => {
799
- for (const candidate of series) {
800
- const value = asNumber(row[candidate.field]);
801
- if (value == null) continue;
802
- if (value > 0) state.hasPositive = true;
803
- else if (value < 0) state.hasNegative = true;
804
- else state.hasZero = true;
805
- }
806
- return state;
807
- },
808
- { hasNegative: false, hasPositive: false, hasZero: false },
809
- );
810
- }
811
-
812
- function shouldColorBarsBySign({
813
- activeSeries,
814
- chart,
815
- signState,
816
- stacked,
817
- }: {
818
- activeSeries: ChartSeriesSpec[];
819
- chart: ChartSpec;
820
- signState: BarSignState;
821
- stacked: boolean;
822
- }): boolean {
823
- if (stacked || activeSeries.length !== 1) return false;
824
- if (chart.type !== "bar" && chart.type !== "horizontalBar") return false;
825
- return signState.hasPositive && signState.hasNegative;
826
- }
827
-
828
- function barSignColor(value: number | null): string {
829
- if (value == null || value === 0) return "var(--ds-chart-series-neutral)";
830
- return value > 0 ? "var(--ds-chart-series-green)" : "var(--ds-chart-series-red)";
831
- }
832
-
833
- function formatSignedValue(value: number, chart: ChartSpec, showPositiveSign: boolean): string {
834
- const label = formatValue(value, chart.valueFormat, chart.unit);
835
- return showPositiveSign && value > 0 ? `+${label}` : label;
836
- }
837
-
838
- function shouldShowBarValueLabels({
839
- activeSeries,
840
- chart,
841
- rows,
842
- stacked,
843
- }: {
844
- activeSeries: ChartSeriesSpec[];
845
- chart: ChartSpec;
846
- rows: ChartDataRow[];
847
- stacked: boolean;
848
- }): boolean {
849
- if (chart.type !== "bar" && chart.type !== "horizontalBar" && chart.type !== "histogram") {
850
- return false;
851
- }
852
- if (stacked) return false;
853
- const labelMode = valueLabelMode(chart);
854
- if (labelMode === "none" || labelMode === "endpoints") return false;
855
- if (labelMode === "all") return true;
856
-
857
- const valueCount = visibleBarValueCount(rows, activeSeries);
858
- return valueCount > 0 && valueCount <= BAR_AUTO_VALUE_LABEL_LIMIT;
859
- }
860
-
861
- function renderBarValueLabel({
862
- chart,
863
- height,
864
- horizontal,
865
- showPositiveSign = false,
866
- value,
867
- width,
868
- x,
869
- y,
870
- }: BarValueLabelProps & { chart: ChartSpec; horizontal: boolean; showPositiveSign?: boolean }) {
871
- const numericValue = asNumber(value);
872
- const numericX = asFiniteNumber(x, Number.NaN);
873
- const numericY = asFiniteNumber(y, Number.NaN);
874
- const numericWidth = asFiniteNumber(width, Number.NaN);
875
- const numericHeight = asFiniteNumber(height, Number.NaN);
876
- if (
877
- numericValue == null ||
878
- ![numericX, numericY, numericWidth, numericHeight].every(Number.isFinite)
879
- ) {
880
- return null;
881
- }
882
-
883
- const label = formatSignedValue(numericValue, chart, showPositiveSign);
884
- const isNegative = numericValue < 0;
885
- if (horizontal) {
886
- const left = Math.min(numericX, numericX + numericWidth);
887
- const right = Math.max(numericX, numericX + numericWidth);
888
- return (
889
- <text
890
- className="chart-bar-value-label"
891
- dominantBaseline="middle"
892
- textAnchor={isNegative ? "end" : "start"}
893
- x={isNegative ? left - BAR_VALUE_LABEL_OFFSET : right + BAR_VALUE_LABEL_OFFSET}
894
- y={numericY + numericHeight / 2}
895
- >
896
- {label}
897
- </text>
898
- );
899
- }
900
-
901
- const top = Math.min(numericY, numericY + numericHeight);
902
- const bottom = Math.max(numericY, numericY + numericHeight);
903
- return (
904
- <text
905
- className="chart-bar-value-label"
906
- dominantBaseline={isNegative ? "hanging" : "auto"}
907
- textAnchor="middle"
908
- x={numericX + numericWidth / 2}
909
- y={isNegative ? bottom + BAR_NEGATIVE_VALUE_LABEL_OFFSET : top - BAR_VALUE_LABEL_OFFSET}
910
- >
911
- {label}
912
- </text>
913
- );
914
- }
915
-
916
- function categoryBarTooltipColor(chart: ChartSpec, rows: ChartDataRow[], item: ChartTooltipPayloadItem, colorBarsBySign = false): string | undefined {
917
- if (colorBarsBySign) return barSignColor(asNumber(item.value));
918
- if (!shouldColorBarsByCategory()) return undefined;
919
- if (chart.series[0]?.color) return undefined;
920
- const payload = item.payload && typeof item.payload === "object"
921
- ? item.payload as ChartDataRow
922
- : null;
923
- const category = payload ? payload[chart.xField] : null;
924
- const rowIndex = rows.findIndex((row) => String(row[chart.xField] ?? "") === String(category ?? ""));
925
- return colorForSeries(undefined, rowIndex < 0 ? 0 : rowIndex);
926
- }
927
-
928
- function getAreaGradientId(chart: ChartSpec, series: ChartSeriesSpec): string {
929
- return `${chart.id}-${series.field}`.replace(/[^a-zA-Z0-9_-]/g, "-");
930
- }
931
-
932
- function referenceLines(chart: ChartSpec, horizontal = false) {
933
- return (chart.referenceLines ?? []).map((line, index) => {
934
- const axis = line.axis ?? (horizontal ? "x" : "y");
935
- const value = typeof line.value === "number" ? line.value : Number(line.value);
936
- if (!Number.isFinite(value)) return null;
937
- const props = {
938
- stroke: "var(--ds-chart-reference-line)",
939
- strokeDasharray: value === 0 ? undefined : "4 4",
940
- };
941
- const key = `${axis}-${value}-${index}`;
942
- return axis === "x" ? <ReferenceLine key={key} {...props} x={value} /> : <ReferenceLine key={key} {...props} y={value} />;
943
- });
944
- }
945
-
946
- function chartLegendItems(chart: ChartSpec, visibleSeries: ChartSeriesSpec[], signState?: BarSignState | null, rows: ChartDataRow[] = []) {
947
- if (signState) {
948
- const items = [];
949
- if (signState.hasPositive) items.push({ color: barSignColor(1), id: "__positive", value: "positive" });
950
- if (signState.hasNegative) items.push({ color: barSignColor(-1), id: "__negative", value: "negative" });
951
- if (signState.hasZero) items.push({ color: barSignColor(0), id: "__zero", value: "neutral" });
952
- return items;
953
- }
954
- if (chart.type === "pie") {
955
- return buildPieRows(chart, rows).map((row) => ({
956
- color: getSeriesColor(chart, chart.series[0], asFiniteNumber(row.__pieIndex)),
957
- id: String(row.__pieField ?? row.__pieName ?? row.__pieIndex),
958
- value: String(row.__pieName ?? row.__pieField ?? ""),
959
- }));
960
- }
961
- if (chart.series.length <= 1) return [];
962
- if (
963
- chart.type !== "area" &&
964
- chart.type !== "bar" &&
965
- chart.type !== "horizontalBar" &&
966
- chart.type !== "line" &&
967
- chart.type !== "scatter" &&
968
- chart.type !== "stackedArea" &&
969
- chart.type !== "stackedBar" &&
970
- chart.type !== "stackedBar100" &&
971
- chart.type !== "horizontalStackedBar" &&
972
- chart.type !== "horizontalStackedBar100" &&
973
- chart.type !== "pie"
974
- ) {
975
- return [];
976
- }
977
- return chart.series.map((series, index) => ({
978
- color: getSeriesColor(chart, series, index),
979
- id: series.field,
980
- lineStyle: series.lineStyle ?? (isTrendChartType(chart.type) && isPlanningSeries(series) ? "dashed" : undefined),
981
- value: series.label ?? series.field,
982
- visible: visibleSeries.some((visible) => visible.field === series.field),
983
- }));
984
- }
985
-
986
- function renderAreaGradientDefs(chart: ChartSpec) {
987
- const shouldRenderAreaFill =
988
- chart.type === "area" || chart.type === "stackedArea" || chart.type === "sparkline";
989
- if (!shouldRenderAreaFill) return null;
990
- return (
991
- <defs>
992
- {chart.series.map((series, index) => {
993
- const color = getSeriesColor(chart, series, index);
994
- const gradientId = getAreaGradientId(chart, series);
995
- return (
996
- <linearGradient id={gradientId} key={gradientId} x1="0" x2="0" y1="0" y2="1">
997
- <stop offset="0%" stopColor={color} stopOpacity={0.12} />
998
- <stop offset="100%" stopColor={color} stopOpacity={0} />
999
- </linearGradient>
1000
- );
1001
- })}
1002
- </defs>
1003
- );
1004
- }
1005
-
1006
- function LeaderboardPanel({ chart, rows }: { chart: ChartSpec; rows: ChartDataRow[] }) {
1007
- const series = chart.series[0];
1008
- const requestedRows = typeof chart.maxRows === "number" && Number.isFinite(chart.maxRows) ? chart.maxRows : 6;
1009
- const rowLimit = Math.max(1, Math.min(8, Math.floor(requestedRows)));
1010
- const rankedRows = [...rows].sort(
1011
- (a, b) => Math.abs(asNumber(b[series?.field ?? ""]) ?? 0) - Math.abs(asNumber(a[series?.field ?? ""]) ?? 0),
1012
- );
1013
- const visibleRows = rankedRows.slice(0, rowLimit);
1014
- const max = Math.max(...visibleRows.map((row) => Math.abs(asNumber(row[series?.field ?? ""]) ?? 0)), 1);
1015
- const style = { "--leaderboard-row-count": visibleRows.length } as CSSProperties;
1016
- return (
1017
- <div className="leaderboard" style={style}>
1018
- {visibleRows.map((row, index) => {
1019
- const value = asNumber(row[series?.field ?? ""]) ?? 0;
1020
- const width = `${Math.max(4, (Math.abs(value) / max) * 100)}%`;
1021
- const rowStyle = {
1022
- "--bar-color": getSeriesColor(chart, series, 0),
1023
- "--bar-width": width,
1024
- } as CSSProperties;
1025
- return (
1026
- <div
1027
- className={`leaderboard-row ${value >= 0 ? "positive" : "negative"}`}
1028
- key={`${String(row[chart.xField])}-${index}`}
1029
- style={rowStyle}
1030
- >
1031
- <span className="leaderboard-label">{String(row[chart.xField] ?? "")}</span>
1032
- <span className="leaderboard-value">{formatValue(value, chart.valueFormat, chart.unit)}</span>
1033
- </div>
1034
- );
1035
- })}
1036
- </div>
1037
- );
1038
- }
1039
-
1040
- function HeatmapPanel({ chart, rows }: { chart: ChartSpec; rows: ChartDataRow[] }) {
1041
- const values = rows.flatMap((row) =>
1042
- chart.series
1043
- .map((series) => asNumber(row[series.field]))
1044
- .filter((value): value is number => value != null),
1045
- );
1046
- if (!values.length) return <div className="empty-state">No numeric heatmap values.</div>;
1047
- const min = Math.min(...values);
1048
- const max = Math.max(...values);
1049
- const range = max - min || 1;
1050
- const heatmapStyle = {
1051
- "--heatmap-row-min-height": `${COMPLEX_CHART_MIN_ROW_HEIGHT}px`,
1052
- "--heatmap-header-row-height": `${HEATMAP_HEADER_ROW_HEIGHT}px`,
1053
- gridTemplateColumns: `minmax(88px, max-content) repeat(${chart.series.length}, minmax(40px, 1fr))`,
1054
- gridTemplateRows: `repeat(${rows.length}, minmax(var(--heatmap-row-min-height), 1fr)) minmax(var(--heatmap-header-row-height), auto)`,
1055
- } as CSSProperties;
1056
-
1057
- return (
1058
- <div className="heatmap-grid-panel">
1059
- <div className="heatmap-grid" style={heatmapStyle}>
1060
- {rows.map((row, rowIndex) => {
1061
- const rowLabel = String(row[chart.xField] ?? "");
1062
- return (
1063
- <Fragment key={`${chart.id}-heatmap-row-${rowIndex}`}>
1064
- <strong key={`${rowIndex}-label`}>{rowLabel}</strong>
1065
- {chart.series.map((series) => {
1066
- const seriesLabel = series.label ?? series.field;
1067
- const value = asNumber(row[series.field]) ?? min;
1068
- const intensity = clamp((value - min) / range, 0, 1);
1069
- const valueLabel = formatValue(value, chart.valueFormat, chart.unit);
1070
- const label = `${rowLabel} ${seriesLabel}: ${valueLabel}`;
1071
- return (
1072
- <span
1073
- aria-label={label}
1074
- className="heatmap-cell"
1075
- key={`${rowIndex}-${series.field}`}
1076
- role="img"
1077
- style={{ background: getHeatmapFill(intensity) }}
1078
- tabIndex={0}
1079
- >
1080
- <span className="chart-tooltip heatmap-tooltip" role="presentation">
1081
- <span className="chart-tooltip-label">{rowLabel}</span>
1082
- <span className="chart-tooltip-items">
1083
- <span className="chart-tooltip-item">
1084
- <span
1085
- aria-hidden="true"
1086
- className="chart-tooltip-marker"
1087
- style={{ background: getHeatmapFill(intensity) }}
1088
- />
1089
- <span className="chart-tooltip-name">{seriesLabel}</span>
1090
- <span className="chart-tooltip-value">{valueLabel}</span>
1091
- </span>
1092
- </span>
1093
- </span>
1094
- </span>
1095
- );
1096
- })}
1097
- </Fragment>
1098
- );
1099
- })}
1100
- <span className="heatmap-axis-corner" />
1101
- {chart.series.map((series) => (
1102
- <b key={series.field}>{series.label ?? series.field}</b>
1103
- ))}
1104
- </div>
1105
- </div>
1106
- );
1107
- }
1108
-
1109
- function CustomPanelFrame({
1110
- chart,
1111
- children,
1112
- height,
1113
- surface,
1114
- }: {
1115
- chart: ChartSpec;
1116
- children: ReactNode;
1117
- height?: number | string;
1118
- surface: ChartSurface;
1119
- }) {
1120
- return (
1121
- <ChartFrame
1122
- className={`chart-frame--custom chart-frame--${chart.type}`}
1123
- height={chartHeightForSurface(surface, height)}
1124
- surface={surface}
1125
- >
1126
- {children}
1127
- </ChartFrame>
1128
- );
1129
- }
1130
-
1131
- export function ChartRenderer({
1132
- chart: chartSpec,
1133
- height,
1134
- onVisibleSeriesChange,
1135
- responsive = true,
1136
- rows: rawRows,
1137
- showLegend = true,
1138
- surface,
1139
- visibleSeries,
1140
- width,
1141
- }: {
1142
- chart: ChartSpec;
1143
- height?: number | string;
1144
- onVisibleSeriesChange?: (visible: Set<string>) => void;
1145
- responsive?: boolean;
1146
- rows: ChartDataRow[];
1147
- showLegend?: boolean;
1148
- surface?: ChartSurface;
1149
- visibleSeries?: Set<string>;
1150
- width?: number | string;
1151
- }) {
1152
- const { chart, rows } = rechartsChartFromEncodedSpec(chartSpec, rawRows);
1153
- const resolvedSurface = surface ?? chart.surface?.surface ?? "card";
1154
- const activeSeries = chart.series.filter((series) => !visibleSeries || !visibleSeries.size || visibleSeries.has(series.field));
1155
- const firstSeries = chart.series[0];
1156
- if (!firstSeries) return <div className="empty-state">Chart needs at least one series.</div>;
1157
-
1158
- if (chart.type === "leaderboard") {
1159
- return <CustomPanelFrame chart={chart} height={height} surface={resolvedSurface}><LeaderboardPanel chart={chart} rows={rows} /></CustomPanelFrame>;
1160
- }
1161
- if (chart.type === "heatmap") {
1162
- return (
1163
- <CustomPanelFrame
1164
- chart={chart}
1165
- height={complexChartFrameHeight({ headerRows: 1, height, rowCount: rows.length, surface: resolvedSurface })}
1166
- surface={resolvedSurface}
1167
- >
1168
- <HeatmapPanel chart={chart} rows={rows} />
1169
- </CustomPanelFrame>
1170
- );
1171
- }
1172
-
1173
- const fixedChartWidth = numericChartDimension(width, CHART_STATIC_WIDTH);
1174
- const xAxisTicks = getDateAxisTicks(rows, chart.xField, fixedChartWidth);
1175
- const barGroupMode = chart.settings?.groupMode;
1176
- const barOrientation = chart.settings?.orientation;
1177
- const horizontal = isHorizontalChartType(chart.type, barOrientation);
1178
- const rotateCategoryXAxisLabels = shouldRotateCategoryXAxisLabels({
1179
- chart,
1180
- horizontal,
1181
- xAxisTicks,
1182
- });
1183
- const wrapCategoryXAxisLabels = shouldWrapCategoryXAxisLabels({
1184
- availableWidth: fixedChartWidth,
1185
- chart,
1186
- horizontal,
1187
- rows,
1188
- xAxisTicks,
1189
- });
1190
- const wrappedCategoryXAxisLineLength = categoryXAxisWrappedLineLength(chart, rows, fixedChartWidth);
1191
- const stacked = isStackedChartType(chart.type, barGroupMode);
1192
- const normalized = isNormalizedStackedChartType(chart.type, barGroupMode);
1193
- const currentBarSignState = barSignState(rows, activeSeries);
1194
- const colorBarsBySign = shouldColorBarsBySign({
1195
- activeSeries,
1196
- chart,
1197
- signState: currentBarSignState,
1198
- stacked,
1199
- });
1200
- const showBarValueLabels = shouldShowBarValueLabels({
1201
- activeSeries,
1202
- chart,
1203
- rows,
1204
- stacked,
1205
- });
1206
- const barValueLabelSides = showBarValueLabels
1207
- ? visibleBarValueLabelSides(rows, activeSeries)
1208
- : { hasNegative: false, hasNonNegative: false };
1209
- const legendItems = showLegend ? chartLegendItems(chart, activeSeries, colorBarsBySign ? currentBarSignState : null, rows) : [];
1210
- const tooltipChart = chart;
1211
- const yAxisScale = getYAxisScaleForSeries(chart, rows, activeSeries);
1212
- const frameHeight = chartFrameHeightForContent({
1213
- hasLegend: legendItems.length > 0,
1214
- height,
1215
- horizontal,
1216
- rotateCategoryXAxisLabels,
1217
- rowCount: rows.length,
1218
- surface: resolvedSurface,
1219
- });
1220
- const commonProps = { data: rows };
1221
- const tooltipWrapperStyle: CSSProperties = { pointerEvents: "none", zIndex: 40 };
1222
- const fastTooltipProps = {
1223
- animationDuration: 0,
1224
- animationEasing: "linear",
1225
- isAnimationActive: false,
1226
- offset: 12,
1227
- wrapperStyle: tooltipWrapperStyle,
1228
- };
1229
- const subtleTooltipCursor = {
1230
- fill: "var(--ds-chart-hover-bg)",
1231
- stroke: "transparent",
1232
- strokeWidth: 0,
1233
- };
1234
- const visibleIds = new Set(activeSeries.map((series) => series.field));
1235
- const interactiveLegend = Boolean(onVisibleSeriesChange) && !colorBarsBySign;
1236
- const fixedFrameHeight = numericChartDimension(
1237
- frameHeight,
1238
- resolvedSurface === "explorer" ? CHART_FULLSCREEN_HEIGHT : CHART_COMPACT_HEIGHT,
1239
- );
1240
- const fixedLegendReserve = legendItems.length ? CHART_LEGEND_RESERVED_HEIGHT : 0;
1241
- const fixedChartHeight = Math.max(CHART_MIN_PLOT_HEIGHT, fixedFrameHeight - fixedLegendReserve);
1242
- function bottomAxisLabel(value: string) {
1243
- return {
1244
- offset: -4,
1245
- position: "insideBottom",
1246
- style: { fill: "var(--ds-text-primary)", fontSize: 12, fontWeight: 500, textAnchor: "middle" },
1247
- value,
1248
- };
1249
- }
1250
-
1251
- function leftAxisLabel(value: string) {
1252
- return {
1253
- angle: -90,
1254
- offset: 0,
1255
- position: "insideLeft",
1256
- style: { fill: "var(--ds-text-primary)", fontSize: 12, fontWeight: 500, textAnchor: "middle" },
1257
- value,
1258
- };
1259
- }
1260
-
1261
- const xAxisLabel = chart.xAxisTitle ? bottomAxisLabel(chart.xAxisTitle) : undefined;
1262
- const yAxisLabel = chart.yAxisTitle ? leftAxisLabel(chart.yAxisTitle) : undefined;
1263
- const horizontalValueAxisLabel = chart.yAxisTitle ? bottomAxisLabel(chart.yAxisTitle) : undefined;
1264
- // Horizontal bars already show category names as row labels; an extra axis title competes for left-side space.
1265
- const horizontalCategoryAxisLabel = undefined;
1266
- const bottomMarginAxisLabel = horizontal ? horizontalValueAxisLabel : xAxisLabel;
1267
- const leftMarginAxisLabel = horizontal ? horizontalCategoryAxisLabel : yAxisLabel;
1268
- const baseBottomMargin = !horizontal && rotateCategoryXAxisLabels ? (bottomMarginAxisLabel ? 16 : 8) : bottomMarginAxisLabel ? 8 : 0;
1269
- const baseLeftMargin = leftMarginAxisLabel ? 4 : 0;
1270
- const cartesianMargin = {
1271
- bottom:
1272
- !horizontal && barValueLabelSides.hasNegative
1273
- ? Math.max(baseBottomMargin, BAR_NEGATIVE_VALUE_LABEL_AXIS_GUTTER)
1274
- : baseBottomMargin,
1275
- left:
1276
- horizontal && barValueLabelSides.hasNegative
1277
- ? Math.max(baseLeftMargin, BAR_VALUE_LABEL_HORIZONTAL_GUTTER)
1278
- : baseLeftMargin,
1279
- right: horizontal && barValueLabelSides.hasNonNegative ? BAR_VALUE_LABEL_HORIZONTAL_GUTTER : 0,
1280
- top:
1281
- !horizontal && barValueLabelSides.hasNonNegative
1282
- ? Math.max(Y_AXIS_TICK_LABEL_OVERHANG, BAR_VALUE_LABEL_VERTICAL_GUTTER)
1283
- : Y_AXIS_TICK_LABEL_OVERHANG,
1284
- };
1285
- const resolveTooltipColor = (item: ChartTooltipPayloadItem) => categoryBarTooltipColor(chart, rows, item, colorBarsBySign);
1286
- const responsiveChartProps = responsive
1287
- ? {
1288
- margin: cartesianMargin,
1289
- }
1290
- : {
1291
- height: fixedChartHeight,
1292
- margin: cartesianMargin,
1293
- width: fixedChartWidth,
1294
- };
1295
- const chartProps = { ...responsiveChartProps, ...commonProps };
1296
- const emptyMessage = chartEmptyMessage(chart, rows, visibleSeries);
1297
- if (emptyMessage) {
1298
- return (
1299
- <ChartFrame
1300
- height={frameHeight}
1301
- interactiveLegend={interactiveLegend}
1302
- legendItems={legendItems}
1303
- onVisibleChange={onVisibleSeriesChange}
1304
- surface={resolvedSurface}
1305
- visibleIds={visibleIds}
1306
- >
1307
- <div className="empty-state">{emptyMessage}</div>
1308
- </ChartFrame>
1309
- );
1310
- }
1311
-
1312
- function renderYAxis(dataKey?: string) {
1313
- const yAxisValueFormat = normalized ? "percent" : chart.valueFormat ?? "compact";
1314
- return (
1315
- <YAxis
1316
- axisLine={false}
1317
- dataKey={dataKey}
1318
- domain={normalized ? [0, 1] : undefined}
1319
- interval={0}
1320
- label={yAxisLabel}
1321
- tick={(props: AxisTickProps) => (
1322
- <NumericYAxisTick {...props} valueFormat={yAxisValueFormat} />
1323
- )}
1324
- tickFormatter={(value) => formatValue(value, yAxisValueFormat)}
1325
- tickLine={false}
1326
- tickMargin={RECHARTS_AXIS_TICK_MARGIN}
1327
- tickSize={RECHARTS_AXIS_TICK_SIZE}
1328
- ticks={yAxisScale.ticks}
1329
- width="auto"
1330
- />
1331
- );
1332
- }
1333
-
1334
- function shouldShowSeriesPoints(series: ChartSeriesSpec): boolean {
1335
- const showPoints = chart.settings?.showPoints;
1336
- if (showPoints === "always") return true;
1337
- if (showPoints === "never") return false;
1338
- let definedPointCount = 0;
1339
- for (const row of rows) {
1340
- if (asNumber(row[series.field]) == null) continue;
1341
- definedPointCount += 1;
1342
- if (definedPointCount > 1) return false;
1343
- }
1344
- return definedPointCount === 1;
1345
- }
1346
-
1347
- function renderSeries() {
1348
- return activeSeries.map((series, index) => {
1349
- const originalIndex = chart.series.findIndex((item) => item.field === series.field);
1350
- const color = getSeriesColor(chart, series, originalIndex < 0 ? index : originalIndex);
1351
- const key = `${chart.id}-${series.field}`;
1352
- if (
1353
- chart.type === "bar" ||
1354
- chart.type === "horizontalBar" ||
1355
- chart.type === "histogram" ||
1356
- chart.type === "stackedBar" ||
1357
- chart.type === "stackedBar100" ||
1358
- chart.type === "horizontalStackedBar" ||
1359
- chart.type === "horizontalStackedBar100"
1360
- ) {
1361
- const isStacked = stacked;
1362
- const isHorizontal = horizontal;
1363
- const isHorizontalStacked = horizontal && stacked;
1364
- const isTopStackSegment = isStacked && index === activeSeries.length - 1;
1365
- return (
1366
- <Bar
1367
- dataKey={series.field}
1368
- fill={color}
1369
- isAnimationActive={false}
1370
- key={key}
1371
- minPointSize={!isStacked && showBarValueLabels ? 2 : undefined}
1372
- name={series.label ?? series.field}
1373
- radius={isStacked ? (isTopStackSegment ? (isHorizontalStacked ? [0, 6, 6, 0] : [6, 6, 0, 0]) : 0) : (isHorizontal ? [0, 6, 6, 0] : [6, 6, 0, 0])}
1374
- stackId={isStacked ? "stack" : undefined}
1375
- >
1376
- {!isStacked ? renderCategoryBarCells(chart, series, rows, colorBarsBySign) : null}
1377
- {showBarValueLabels ? (
1378
- <LabelList
1379
- content={(props) => renderBarValueLabel({ ...props, chart, horizontal: isHorizontal, showPositiveSign: colorBarsBySign })}
1380
- dataKey={series.field}
1381
- />
1382
- ) : null}
1383
- </Bar>
1384
- );
1385
- }
1386
- if (chart.type === "area" || chart.type === "stackedArea" || chart.type === "sparkline") {
1387
- const strokeDasharray = getSeriesStrokeDasharray(chart, series);
1388
- return (
1389
- <Area
1390
- dataKey={series.field}
1391
- dot={shouldShowSeriesPoints(series)}
1392
- fill={`url(#${getAreaGradientId(chart, series)})`}
1393
- fillOpacity={1}
1394
- isAnimationActive={false}
1395
- key={key}
1396
- name={series.label ?? series.field}
1397
- stackId={chart.type === "stackedArea" ? "stack" : undefined}
1398
- stroke={color}
1399
- strokeDasharray={strokeDasharray}
1400
- strokeWidth={2}
1401
- type="monotone"
1402
- />
1403
- );
1404
- }
1405
- const strokeDasharray = getSeriesStrokeDasharray(chart, series);
1406
- return (
1407
- <Line
1408
- dataKey={series.field}
1409
- dot={shouldShowSeriesPoints(series)}
1410
- isAnimationActive={false}
1411
- key={key}
1412
- name={series.label ?? series.field}
1413
- stroke={color}
1414
- strokeDasharray={strokeDasharray}
1415
- strokeWidth={2}
1416
- type="monotone"
1417
- />
1418
- );
1419
- });
1420
- }
1421
-
1422
- const xAxisProps = {
1423
- angle: rotateCategoryXAxisLabels ? CATEGORY_X_AXIS_LABEL_ROTATION_DEGREES : undefined,
1424
- axisLine: false,
1425
- dataKey: chart.xField,
1426
- height: rotateCategoryXAxisLabels
1427
- ? xAxisLabel
1428
- ? CATEGORY_X_AXIS_ROTATED_HEIGHT_WITH_TITLE
1429
- : CATEGORY_X_AXIS_ROTATED_HEIGHT
1430
- : wrapCategoryXAxisLabels
1431
- ? xAxisLabel
1432
- ? CATEGORY_X_AXIS_WRAPPED_HEIGHT_WITH_TITLE
1433
- : CATEGORY_X_AXIS_WRAPPED_HEIGHT
1434
- : undefined,
1435
- interval: rotateCategoryXAxisLabels || wrapCategoryXAxisLabels || xAxisTicks ? 0 : undefined,
1436
- label: xAxisLabel,
1437
- tick: xAxisTicks
1438
- ? ((props: AxisTickProps) => (
1439
- <XAxisEndpointTick
1440
- {...props}
1441
- firstTick={xAxisTicks[0]}
1442
- lastTick={xAxisTicks[xAxisTicks.length - 1]}
1443
- />
1444
- ))
1445
- : wrapCategoryXAxisLabels
1446
- ? ((props: AxisTickProps) => (
1447
- <WrappedCategoryXAxisTick
1448
- {...props}
1449
- maxLineLength={wrappedCategoryXAxisLineLength}
1450
- />
1451
- ))
1452
- : undefined,
1453
- tickLine: false,
1454
- tickMargin: rotateCategoryXAxisLabels
1455
- ? CATEGORY_X_AXIS_ROTATED_TICK_MARGIN
1456
- : wrapCategoryXAxisLabels
1457
- ? CATEGORY_X_AXIS_WRAPPED_TICK_MARGIN
1458
- : RECHARTS_AXIS_TICK_MARGIN,
1459
- tickSize: RECHARTS_AXIS_TICK_SIZE,
1460
- ticks: xAxisTicks,
1461
- textAnchor: rotateCategoryXAxisLabels ? "end" : undefined,
1462
- };
1463
-
1464
- const boxPlotRows = chart.type === "boxPlot" ? buildBoxPlotRows(chart, rows) : [];
1465
- const boxPlotXAxisScale = chart.type === "boxPlot" ? getBoxPlotXAxisScale(boxPlotRows) : null;
1466
- const waterfallRows = chart.type === "waterfall" ? buildWaterfallRows(chart, rows) : [];
1467
- const funnelRows = chart.type === "funnel" ? buildFunnelRows(chart, rows, getFunnelStageColor) : [];
1468
- const pieRows = chart.type === "pie"
1469
- ? buildPieRows(chart, rows).filter((row) => chart.series.length <= 1 || visibleIds.has(String(row.__pieField ?? "")))
1470
- : [];
1471
- const scatterYField = chart.type === "scatter" ? chartEncodingField(chart, "y") : undefined;
1472
- const scatterLabelField = chart.type === "scatter" ? chartEncodingField(chart, "label") : undefined;
1473
- const scatterSizeField = chart.type === "scatter" ? chartEncodingField(chart, "size") : undefined;
1474
- const scatterLabelCount = scatterLabelField
1475
- ? rows.filter((row) => row[scatterLabelField] != null && String(row[scatterLabelField]).trim()).length
1476
- : 0;
1477
- const showScatterPointLabels = Boolean(
1478
- scatterLabelField &&
1479
- chart.labels?.values !== "none" &&
1480
- (chart.labels?.values === "all" || scatterLabelCount <= SCATTER_AUTO_LABEL_LIMIT),
1481
- );
1482
-
1483
- const chartElement =
1484
- chart.type === "boxPlot" ? (
1485
- <BarChart {...chartProps} data={boxPlotRows} layout="vertical" margin={{ ...cartesianMargin, top: 0 }}>
1486
- <CartesianGrid horizontal={false} />
1487
- <XAxis axisLine={false} domain={boxPlotXAxisScale?.domain} label={horizontalValueAxisLabel} tickFormatter={(value) => formatValue(value, chart.valueFormat)} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} ticks={boxPlotXAxisScale?.ticks} type="number" />
1488
- <YAxis axisLine={false} dataKey={chart.xField} interval={0} label={horizontalCategoryAxisLabel} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} type="category" width="auto" />
1489
- <Tooltip {...fastTooltipProps} content={<BoxPlotTooltip chart={tooltipChart} />} cursor={false} />
1490
- <Bar barSize={22} dataKey="__boxRange" fill="var(--ds-chart-series-blue)" isAnimationActive={false} name="Quartile range" radius={[6, 6, 6, 6]} />
1491
- </BarChart>
1492
- ) : chart.type === "waterfall" ? (
1493
- <BarChart {...chartProps} data={waterfallRows} margin={cartesianMargin}>
1494
- <CartesianGrid vertical={false} />
1495
- <XAxis {...xAxisProps} />
1496
- <YAxis axisLine={false} interval={0} label={yAxisLabel} tick={(props: AxisTickProps) => (<NumericYAxisTick {...props} valueFormat={chart.valueFormat ?? "compact"} />)} tickFormatter={(value) => formatValue(value, chart.valueFormat)} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} width="auto" />
1497
- <ReferenceLine y={0} stroke="var(--ds-chart-reference-line)" strokeWidth={1} />
1498
- <Tooltip {...fastTooltipProps} content={<WaterfallTooltip chart={tooltipChart} />} cursor={subtleTooltipCursor} />
1499
- <Bar
1500
- background={{ fill: "transparent" }}
1501
- dataKey="__waterfallRange"
1502
- isAnimationActive={false}
1503
- name={firstSeries.label ?? firstSeries.field}
1504
- shape={renderWaterfallBarShape}
1505
- />
1506
- </BarChart>
1507
- ) : horizontal && !stacked && (chart.type === "bar" || chart.type === "horizontalBar") ? (
1508
- <BarChart {...chartProps} layout="vertical" margin={{ ...cartesianMargin, top: 0 }}>
1509
- <CartesianGrid horizontal={false} />
1510
- <XAxis axisLine={false} label={horizontalValueAxisLabel} tickFormatter={(value) => formatValue(value, chart.valueFormat)} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} ticks={yAxisScale.ticks} type="number" />
1511
- <YAxis axisLine={false} dataKey={chart.xField} interval={0} label={horizontalCategoryAxisLabel} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} type="category" width="auto" />
1512
- {referenceLines(chart, true)}
1513
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1514
- {renderSeries()}
1515
- </BarChart>
1516
- ) : horizontal && stacked && (
1517
- chart.type === "bar" ||
1518
- chart.type === "horizontalStackedBar" ||
1519
- chart.type === "horizontalStackedBar100"
1520
- ) ? (
1521
- <BarChart {...chartProps} layout="vertical" margin={cartesianMargin} stackOffset={normalized ? "expand" : undefined}>
1522
- <CartesianGrid horizontal={false} />
1523
- <XAxis axisLine={false} domain={normalized ? [0, 1] : undefined} label={horizontalValueAxisLabel} tickFormatter={(value) => formatValue(value, normalized ? "percent" : chart.valueFormat)} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} type="number" />
1524
- <YAxis axisLine={false} dataKey={chart.xField} interval={0} label={horizontalCategoryAxisLabel} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} type="category" width="auto" />
1525
- {referenceLines(chart, true)}
1526
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1527
- {renderSeries()}
1528
- </BarChart>
1529
- ) : chart.type === "funnel" ? (
1530
- <FunnelChart {...chartProps} data={funnelRows} margin={{ bottom: 0, left: 24, right: 24, top: 0 }}>
1531
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1532
- <Funnel
1533
- data={funnelRows}
1534
- dataKey={firstSeries.field}
1535
- fill={FUNNEL_STAGE_COLORS[0]}
1536
- isAnimationActive={false}
1537
- nameKey={chart.xField}
1538
- shape={renderRoundedFunnelShape}
1539
- stroke="none"
1540
- >
1541
- {funnelRows.map((row, index) => (
1542
- <Cell key={`${chart.id}-funnel-stage-${index}`} fill={String(row.__funnelStageColor)} />
1543
- ))}
1544
- <LabelList content={renderFunnelCenterLabel} dataKey="__funnelPairLabel" position="center" />
1545
- </Funnel>
1546
- </FunnelChart>
1547
- ) : chart.type === "pie" ? (
1548
- <PieChart {...chartProps} data={pieRows} margin={{ bottom: 0, left: 0, right: 0, top: 0 }}>
1549
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1550
- <Pie data={pieRows} dataKey="__pieValue" innerRadius="42%" isAnimationActive={false} nameKey="__pieName" outerRadius="78%">
1551
- {pieRows.map((row) => (
1552
- <Cell fill={getSeriesColor(chart, firstSeries, asFiniteNumber(row.__pieIndex))} key={`${chart.id}-pie-${row.__pieIndex}`} />
1553
- ))}
1554
- </Pie>
1555
- </PieChart>
1556
- ) : chart.type === "sparkline" ? (
1557
- <AreaChart {...chartProps} margin={{ bottom: 20, left: 8, right: 8, top: 18 }}>
1558
- {renderAreaGradientDefs(chart)}
1559
- <XAxis dataKey={chart.xField} hide />
1560
- <YAxis hide />
1561
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1562
- <Area dataKey={firstSeries.field} dot={false} fill={`url(#${getAreaGradientId(chart, firstSeries)})`} fillOpacity={1} isAnimationActive={false} name={firstSeries.label ?? firstSeries.field} stroke={getSeriesColor(chart, firstSeries, 0)} strokeDasharray={getSeriesStrokeDasharray(chart, firstSeries)} strokeWidth={2} type="monotone" />
1563
- </AreaChart>
1564
- ) : chart.type === "histogram" || (!horizontal && (
1565
- chart.type === "bar" ||
1566
- chart.type === "stackedBar" ||
1567
- chart.type === "stackedBar100"
1568
- )) ? (
1569
- <BarChart {...chartProps} barCategoryGap={chart.type === "histogram" ? 1 : undefined} stackOffset={normalized ? "expand" : undefined}>
1570
- {renderAreaGradientDefs(chart)}
1571
- <CartesianGrid vertical={false} />
1572
- <XAxis {...xAxisProps} />
1573
- {renderYAxis()}
1574
- {referenceLines(chart)}
1575
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1576
- {renderSeries()}
1577
- </BarChart>
1578
- ) : chart.type === "scatter" ? (
1579
- <ScatterChart {...chartProps}>
1580
- <CartesianGrid vertical={false} />
1581
- <XAxis axisLine={false} dataKey={chart.xField} label={xAxisLabel} name={chart.xAxisTitle ?? chart.xField} tickLine={false} tickMargin={RECHARTS_AXIS_TICK_MARGIN} tickSize={RECHARTS_AXIS_TICK_SIZE} type="number" />
1582
- {renderYAxis(scatterYField ?? firstSeries.field)}
1583
- {scatterSizeField ? <ZAxis dataKey={scatterSizeField} range={[72, 420]} type="number" /> : null}
1584
- {referenceLines(chart)}
1585
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1586
- {activeSeries.map((series, index) => {
1587
- const originalIndex = chart.series.findIndex((item) => item.field === series.field);
1588
- const seriesRows = rows.filter((row) => asNumber(row[series.field]) != null);
1589
- const scatterDataKey = scatterYField && seriesRows.some((row) => asNumber(row[scatterYField]) != null)
1590
- ? scatterYField
1591
- : series.field;
1592
- return (
1593
- <Scatter
1594
- data={seriesRows}
1595
- dataKey={scatterDataKey}
1596
- fill={getSeriesColor(chart, series, originalIndex < 0 ? index : originalIndex)}
1597
- isAnimationActive={false}
1598
- key={`${chart.id}-${series.field}`}
1599
- name={series.label ?? series.field}
1600
- >
1601
- {showScatterPointLabels && scatterLabelField ? (
1602
- <LabelList content={renderScatterPointLabel} dataKey={scatterLabelField} />
1603
- ) : null}
1604
- </Scatter>
1605
- );
1606
- })}
1607
- </ScatterChart>
1608
- ) : chart.type === "area" || chart.type === "stackedArea" ? (
1609
- <AreaChart {...chartProps}>
1610
- {renderAreaGradientDefs(chart)}
1611
- <CartesianGrid vertical={false} />
1612
- <XAxis {...xAxisProps} />
1613
- {renderYAxis()}
1614
- {referenceLines(chart)}
1615
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1616
- {renderSeries()}
1617
- </AreaChart>
1618
- ) : (
1619
- <LineChart {...chartProps}>
1620
- <CartesianGrid vertical={false} />
1621
- <XAxis {...xAxisProps} />
1622
- {renderYAxis()}
1623
- {referenceLines(chart)}
1624
- <Tooltip {...fastTooltipProps} content={<ChartTooltip chart={tooltipChart} getItemColor={resolveTooltipColor} />} cursor={subtleTooltipCursor} />
1625
- {renderSeries()}
1626
- </LineChart>
1627
- );
1628
-
1629
- return (
1630
- <ChartFrame
1631
- height={frameHeight}
1632
- interactiveLegend={interactiveLegend}
1633
- legendItems={legendItems}
1634
- onVisibleChange={onVisibleSeriesChange}
1635
- surface={resolvedSurface}
1636
- visibleIds={visibleIds}
1637
- >
1638
- {responsive ? (
1639
- <ResponsiveContainer height="100%" initialDimension={{ height: 1, width: 1 }} width="100%">
1640
- {chartElement}
1641
- </ResponsiveContainer>
1642
- ) : (
1643
- chartElement
1644
- )}
1645
- </ChartFrame>
1646
- );
1647
- }