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.
- package/package.json +1 -1
- package/src/template/asset-runtime.js +3 -1
- package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
- package/template/.agents/skills/codebase-design/SKILL.md +4 -0
- package/template/{.codex/skills/improve-codebase-architecture/SKILL.md → .agents/skills/codebase-design/references/architecture-audit.md} +3 -9
- package/template/.agents/skills/frontend-commit/SKILL.md +4 -93
- package/template/.agents/skills/git-commit-core/SKILL.md +18 -0
- package/template/.agents/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.agents/skills/llm-wiki/SKILL.md +12 -13
- package/template/.agents/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.agents/skills/llm-wiki/references/compile.md +21 -114
- package/template/.agents/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.agents/skills/llm-wiki/references/lint.md +9 -46
- package/template/.agents/skills/llm-wiki/references/query.md +9 -18
- package/template/.agents/skills/llm-wiki/references/schema.md +56 -29
- package/template/.agents/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.agents/skills/llm-wiki/references/writing.md +4 -4
- package/template/.agents/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.agents/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.agents/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.agents/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.agents/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.agents/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.agents/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.agents/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.agents/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.agents/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.agents/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.agents/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.agents/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.agents/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.agents/skills/prototype/LOGIC.md +1 -1
- package/template/.agents/skills/prototype/SKILL.md +5 -19
- package/template/.agents/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.agents/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.agents/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.agents/skills/yss-research/SKILL.md +4 -0
- package/template/.codex/skills/codebase-design/SKILL.md +4 -0
- package/template/{.pi/skills/improve-codebase-architecture/SKILL.md → .codex/skills/codebase-design/references/architecture-audit.md} +3 -9
- package/template/.codex/skills/frontend-commit/SKILL.md +4 -93
- package/template/.codex/skills/git-commit-core/SKILL.md +18 -0
- package/template/.codex/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.codex/skills/llm-wiki/SKILL.md +12 -13
- package/template/.codex/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.codex/skills/llm-wiki/references/compile.md +21 -114
- package/template/.codex/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.codex/skills/llm-wiki/references/lint.md +9 -46
- package/template/.codex/skills/llm-wiki/references/query.md +9 -18
- package/template/.codex/skills/llm-wiki/references/schema.md +56 -29
- package/template/.codex/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.codex/skills/llm-wiki/references/writing.md +4 -4
- package/template/.codex/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.codex/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.codex/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.codex/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.codex/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.codex/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.codex/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.codex/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.codex/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.codex/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.codex/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.codex/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.codex/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.codex/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.codex/skills/prototype/LOGIC.md +1 -1
- package/template/.codex/skills/prototype/SKILL.md +5 -19
- package/template/.codex/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.codex/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.codex/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.codex/skills/yss-research/SKILL.md +4 -0
- package/template/.cursor/skills/codebase-design/SKILL.md +4 -0
- package/template/.cursor/skills/{improve-codebase-architecture/SKILL.md → codebase-design/references/architecture-audit.md} +3 -9
- package/template/.cursor/skills/frontend-commit/SKILL.md +4 -93
- package/template/.cursor/skills/git-commit-core/SKILL.md +18 -0
- package/template/.cursor/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.cursor/skills/llm-wiki/SKILL.md +12 -13
- package/template/.cursor/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.cursor/skills/llm-wiki/references/compile.md +21 -114
- package/template/.cursor/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.cursor/skills/llm-wiki/references/lint.md +9 -46
- package/template/.cursor/skills/llm-wiki/references/query.md +9 -18
- package/template/.cursor/skills/llm-wiki/references/schema.md +56 -29
- package/template/.cursor/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.cursor/skills/llm-wiki/references/writing.md +4 -4
- package/template/.cursor/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.cursor/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.cursor/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.cursor/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.cursor/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.cursor/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.cursor/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.cursor/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.cursor/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.cursor/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.cursor/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.cursor/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.cursor/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.cursor/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.cursor/skills/prototype/LOGIC.md +1 -1
- package/template/.cursor/skills/prototype/SKILL.md +5 -19
- package/template/.cursor/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.cursor/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.cursor/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.cursor/skills/yss-research/SKILL.md +4 -0
- package/template/.pi/skills/codebase-design/SKILL.md +4 -0
- package/template/{.agents/skills/improve-codebase-architecture/SKILL.md → .pi/skills/codebase-design/references/architecture-audit.md} +3 -9
- package/template/.pi/skills/frontend-commit/SKILL.md +4 -93
- package/template/.pi/skills/git-commit-core/SKILL.md +18 -0
- package/template/.pi/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.pi/skills/llm-wiki/SKILL.md +12 -13
- package/template/.pi/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.pi/skills/llm-wiki/references/compile.md +21 -114
- package/template/.pi/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.pi/skills/llm-wiki/references/lint.md +9 -46
- package/template/.pi/skills/llm-wiki/references/query.md +9 -18
- package/template/.pi/skills/llm-wiki/references/schema.md +56 -29
- package/template/.pi/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.pi/skills/llm-wiki/references/writing.md +4 -4
- package/template/.pi/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.pi/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.pi/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.pi/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.pi/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.pi/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.pi/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.pi/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.pi/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.pi/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.pi/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.pi/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.pi/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.pi/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.pi/skills/prototype/LOGIC.md +1 -1
- package/template/.pi/skills/prototype/SKILL.md +5 -19
- package/template/.pi/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.pi/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.pi/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.pi/skills/yss-research/SKILL.md +4 -0
- package/template/.template-spec/agents/README.md +1 -1
- package/template/.template-spec/agents/digital-human-roles.yaml +1 -1
- package/template/.template-spec/agents/skill-migrations.md +15 -0
- package/template/.template-spec/agents/yss-skill-registry.yaml +10 -29
- package/template/.template-spec/architecture/README.md +3 -34
- package/template/.template-spec/plan/templates/competitive-analysis-template.md +2 -2
- package/template/.template-spec/process/contract-reading.md +24 -0
- package/template/.template-spec/process/lifecycle-registry-baseline.json +3 -1
- package/template/.template-spec/process/lifecycle-registry.yaml +6 -0
- package/template/.template-spec/process/research-completion.md +23 -0
- package/template/.template-spec/process/schemas/digital-human-task-package.schema.json +202 -12
- package/template/.template-spec/process/schemas/frontend-implementation-evidence.schema.json +7 -15
- package/template/.template-spec/process/subagent-collaboration.md +8 -0
- package/template/.template-spec/process/templates/frontend-implementation-verification-template.yaml +15 -1
- package/template/AGENTS.md +1 -1
- package/template/CONTEXT.md +5 -1
- package/template/README.md +12 -24
- package/template/scripts/contract +5 -5
- package/template/scripts/lib/contract-views.mjs +57 -2
- package/template/scripts/lib/execution-evidence.mjs +51 -0
- package/template/scripts/lib/harness-execution-scope.mjs +1 -1
- package/template/scripts/lib/implementation-contract-compiler.mjs +2 -0
- package/template/scripts/lib/lifecycle-context-query.mjs +15 -3
- package/template/scripts/lib/lifecycle-status.mjs +32 -6
- package/template/scripts/lib/lifecycle-transition.mjs +19 -1
- package/template/scripts/lib/maintenance-research.mjs +63 -0
- package/template/scripts/lib/read-only-intake.mjs +132 -0
- package/template/scripts/lib/skill-registry.mjs +2 -1
- package/template/scripts/lib/skill-supply-chain.mjs +4 -0
- package/template/scripts/lib/slice-task-package.mjs +1 -1
- package/template/scripts/lib/task-package.mjs +9 -2
- package/template/scripts/lifecycle-status +2 -2
- package/template/scripts/prepare-read-only-intake +16 -0
- package/template/scripts/run-read-only-intake +14 -0
- package/template/scripts/verify-digital-human-task-package +2 -2
- package/template/scripts/verify-frontend-implementation-evidence +24 -1
- package/template/scripts/verify-maintenance-research +13 -0
- package/template/skills-lock.json +19 -76
- package/template.manifest.json +118 -29
- package/template.snapshot.json +5 -6
- package/template/.agents/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.agents/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.agents/skills/prototype/UI.md +0 -112
- package/template/.agents/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.agents/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.agents/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.agents/skills/wait-what/SKILL.md +0 -7
- package/template/.agents/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.codex/skills/data-analytics/.app.json +0 -84
- package/template/.codex/skills/data-analytics/.codex-plugin/plugin.json +0 -66
- package/template/.codex/skills/data-analytics/.mcp.json +0 -30
- package/template/.codex/skills/data-analytics/AGENTS.md +0 -20
- package/template/.codex/skills/data-analytics/DEPENDENCIES.MD +0 -27
- package/template/.codex/skills/data-analytics/README.md +0 -60
- package/template/.codex/skills/data-analytics/__yss_dotfile__.gitignore +0 -2
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part005 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part006 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part005 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part006 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part007 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-small.svg +0 -5
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience.png +0 -0
- package/template/.codex/skills/data-analytics/assets/datascience.svg +0 -10
- package/template/.codex/skills/data-analytics/mcp/server.cjs +0 -2964
- package/template/.codex/skills/data-analytics/package-lock.json +0 -3048
- package/template/.codex/skills/data-analytics/package.json +0 -43
- package/template/.codex/skills/data-analytics/scripts/normalize-widget-assets.mjs +0 -75
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +0 -137
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/references/quality-checks.md +0 -29
- package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +0 -148
- package/template/.codex/skills/data-analytics/skills/build-dashboard/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/bi-platform-dashboard.md +0 -18
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/html-dashboard.md +0 -24
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/mcp-artifact-dashboard.md +0 -71
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/streamlit-dashboard.md +0 -85
- package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +0 -207
- package/template/.codex/skills/data-analytics/skills/build-report/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/assets/executive-report-shell.html +0 -70
- package/template/.codex/skills/data-analytics/skills/build-report/assets/technical-report-shell.html +0 -66
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/SKILL.md +0 -108
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/__init__.py +0 -1
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/cli.py +0 -52
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/constants.py +0 -61
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/docx_writer.py +0 -362
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/html_parser.py +0 -829
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/model.py +0 -30
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/plan.py +0 -127
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/quality.py +0 -374
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/rendering.py +0 -613
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/table_utils.py +0 -54
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/utils.py +0 -16
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc_plan.py +0 -9
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/tests/test_delivery_plan.py +0 -45
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/SKILL.md +0 -77
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/scripts/report_to_google_slides.py +0 -2379
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/SKILL.md +0 -88
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/executive-report.md +0 -97
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/mcp-app-report.md +0 -59
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/technical-report.md +0 -75
- package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +0 -103
- package/template/.codex/skills/data-analytics/skills/design-kpis/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +0 -68
- package/template/.codex/skills/data-analytics/skills/gather-business-context/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/index/SKILL.md +0 -251
- package/template/.codex/skills/data-analytics/skills/index/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +0 -131
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +0 -141
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/references/report-templates.md +0 -32
- package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +0 -106
- package/template/.codex/skills/data-analytics/skills/market-sizing/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +0 -130
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +0 -141
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +0 -178
- package/template/.codex/skills/data-analytics/skills/spreadsheets/agents/openai.yaml +0 -9
- package/template/.codex/skills/data-analytics/skills/spreadsheets/assets/file-spreadsheet.png +0 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/charts.md +0 -31
- package/template/.codex/skills/data-analytics/skills/spreadsheets/references/artifact_tool_api.md +0 -466
- package/template/.codex/skills/data-analytics/skills/spreadsheets/style_guidelines.md +0 -99
- package/template/.codex/skills/data-analytics/skills/user-context/SKILL.md +0 -197
- package/template/.codex/skills/data-analytics/skills/user-context/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/automation-config.md +0 -26
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/source-category-config.json +0 -51
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/user-context-config.md +0 -66
- package/template/.codex/skills/data-analytics/skills/user-context/references/automation.md +0 -69
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-examples.md +0 -204
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-state-template.json +0 -87
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding.md +0 -497
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/connector-playbook.md +0 -74
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/setup.md +0 -65
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/skill-template.md +0 -160
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/source-intake.md +0 -75
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/weekly-polling-automation.md +0 -96
- package/template/.codex/skills/data-analytics/skills/user-context/references/source-category-runtime.md +0 -263
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/data_analytics_preflight.py +0 -1462
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/init_user_context_state.py +0 -128
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/reset_user_context_state.py +0 -101
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/validate_user_context_preflight.py +0 -499
- package/template/.codex/skills/data-analytics/skills/user-context/tests/test_state_helpers.py +0 -978
- package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +0 -126
- package/template/.codex/skills/data-analytics/skills/validate-data/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/validate-data/references/validation-methods.md +0 -70
- package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +0 -157
- package/template/.codex/skills/data-analytics/skills/visualize-data/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/visualize-data/references/seaborn-templates.md +0 -774
- package/template/.codex/skills/data-analytics/src/DESIGN.md +0 -222
- package/template/.codex/skills/data-analytics/src/analytics-app/App.tsx +0 -3666
- package/template/.codex/skills/data-analytics/src/analytics-app/analytics-layout.test.mjs +0 -136
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartFrame.tsx +0 -54
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartLegend.tsx +0 -96
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartRenderer.tsx +0 -1647
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartTooltip.tsx +0 -245
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-app-helpers.tsx +0 -462
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-capabilities.ts +0 -107
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-compatibility.ts +0 -164
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-contract.ts +0 -192
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-theme.ts +0 -203
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-tokens.css +0 -619
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-transforms.ts +0 -402
- package/template/.codex/skills/data-analytics/src/analytics-app/fonts/SystemSansVariableVF.woff2 +0 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/imageExport.ts +0 -370
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/AnalyticsLayoutCanvas.tsx +0 -536
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/RichMarkdown.tsx +0 -681
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/analyticsLayoutCore.ts +0 -164
- package/template/.codex/skills/data-analytics/src/analytics-app/main.tsx +0 -21
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/chart_contract.py +0 -472
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/check_analytics_app_runtime_links.py +0 -75
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/design_contract.py +0 -142
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/package_utils.py +0 -142
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/tests/test_package_utils.py +0 -377
- package/template/.codex/skills/data-analytics/src/analytics-app/styles.css +0 -3468
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.d.ts +0 -44
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.jsx +0 -639
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/data-table.css +0 -327
- package/template/.codex/skills/data-analytics/src/analytics-app/tokens.css +0 -225
- package/template/.codex/skills/data-analytics/src/analytics-app/types.ts +0 -204
- package/template/.codex/skills/data-analytics/src/analytics-app-core.md +0 -85
- package/template/.codex/skills/data-analytics/src/codex-style-contract.md +0 -30
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.html +0 -12
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.jsx +0 -1008
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.css +0 -2117
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.html +0 -122
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.js +0 -3588
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.css +0 -483
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.html +0 -32
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.js +0 -333
- package/template/.codex/skills/data-analytics/src/mcp-host.js +0 -153
- package/template/.codex/skills/data-analytics/src/recharts-config.js +0 -315
- package/template/.codex/skills/data-analytics/src/recharts-renderer.jsx +0 -294
- package/template/.codex/skills/data-analytics/src/sql-source-view.css +0 -33
- package/template/.codex/skills/data-analytics/src/sql-source-view.js +0 -157
- package/template/.codex/skills/data-analytics/src/styles/codex-theme.css +0 -121
- package/template/.codex/skills/data-analytics/src/table-renderer.jsx +0 -28
- package/template/.codex/skills/data-analytics/tests/chart-transforms.test.mjs +0 -87
- package/template/.codex/skills/data-analytics/tests/funnel-smoke.html +0 -105
- package/template/.codex/skills/data-analytics/tests/inline-widget-compare.html +0 -233
- package/template/.codex/skills/data-analytics/tests/mcp-server.test.mjs +0 -1500
- package/template/.codex/skills/data-analytics/tests/native-style-contract.test.mjs +0 -219
- package/template/.codex/skills/data-analytics/tests/recharts-config.test.mjs +0 -144
- package/template/.codex/skills/data-analytics/tests/recharts-renderer.test.mjs +0 -11
- package/template/.codex/skills/data-analytics/tests/rich-markdown.test.mjs +0 -54
- package/template/.codex/skills/data-analytics/tests/widget-render-harness.html +0 -321
- package/template/.codex/skills/data-analytics/tsconfig.json +0 -19
- package/template/.codex/skills/data-analytics/vite.config.ts +0 -46
- package/template/.codex/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.codex/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.codex/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.codex/skills/prototype/UI.md +0 -112
- package/template/.codex/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.codex/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.codex/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.codex/skills/wait-what/SKILL.md +0 -7
- package/template/.codex/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.cursor/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.cursor/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.cursor/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.cursor/skills/prototype/UI.md +0 -112
- package/template/.cursor/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.cursor/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.cursor/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.cursor/skills/wait-what/SKILL.md +0 -7
- package/template/.cursor/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.pi/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.pi/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.pi/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.pi/skills/prototype/UI.md +0 -112
- package/template/.pi/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.pi/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.pi/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.pi/skills/wait-what/SKILL.md +0 -7
- package/template/.pi/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.template-spec/agents/domain.md +0 -31
- /package/template/.agents/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.codex/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.cursor/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /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
|
-
}
|