From 49a9e921954e7455fa16381af217883bc0ca8a1a Mon Sep 17 00:00:00 2001 From: "jiangtao.young" Date: Tue, 24 Mar 2026 11:05:56 +0800 Subject: [PATCH 1/5] feat: refactor builder to chart-builder and add report builder support - Refactor builder directory to chart-builder for better organization - Add new report-builder package with page and text builder features - Add report DSL types (report, page, text) - Update documentation structure for chartBuilder and reportBuilder APIs - Update VBI types and exports to support new architecture Co-Authored-By: Claude Opus 4.6 --- apps/website/docs/zh-CN/vbi/api/_meta.json | 60 +-- .../zh-CN/vbi/api/chartBuilder/_meta.json | 55 +++ .../chartType.md} | 2 +- .../api/chartBuilder/dimensions/_meta.json | 7 + .../dimensions/dimensionNode.md} | 2 + .../dimensions/index.md} | 2 +- .../api/chartBuilder/havingFilter/_meta.json | 12 + .../havingFilter/havingGroup.md} | 2 + .../havingFilter/havingNode.md} | 2 + .../havingFilter/index.md} | 2 +- .../api/{builder.md => chartBuilder/index.md} | 3 +- .../zh-CN/vbi/api/{ => chartBuilder}/limit.md | 2 +- .../vbi/api/{ => chartBuilder}/locale.md | 2 +- .../vbi/api/chartBuilder/measures/_meta.json | 7 + .../measures/index.md} | 2 +- .../measures/measureNode.md} | 2 + .../zh-CN/vbi/api/{ => chartBuilder}/theme.md | 2 +- .../undoManager.md} | 2 +- .../api/chartBuilder/whereFilter/_meta.json | 12 + .../whereFilter/index.md} | 2 +- .../whereFilter/whereGroup.md} | 2 + .../whereFilter/whereNode.md} | 4 +- .../docs/zh-CN/vbi/api/dimensions/_meta.json | 8 - .../zh-CN/vbi/api/having-filter/_meta.json | 14 - apps/website/docs/zh-CN/vbi/api/index.md | 1 + .../docs/zh-CN/vbi/api/measures/_meta.json | 8 - .../zh-CN/vbi/api/reportBuilder/_meta.json | 9 + .../docs/zh-CN/vbi/api/reportBuilder/index.md | 81 ++++ .../vbi/api/reportBuilder/page/_meta.json | 12 + .../zh-CN/vbi/api/reportBuilder/page/index.md | 88 +++++ .../vbi/api/reportBuilder/page/reportPage.md | 95 +++++ .../vbi/api/reportBuilder/page/reportText.md | 65 +++ .../zh-CN/vbi/api/where-filter/_meta.json | 14 - packages/vbi/docs/todo3-create-report/adr.md | 116 ++++++ packages/vbi/docs/todo3-create-report/goal.md | 5 +- packages/vbi/docs/todo3-create-report/plan.md | 110 ++++++ packages/vbi/scripts/build-api.mjs | 374 +++++++++++------- .../adapters/index.ts | 0 .../adapters/vquery-vseed/build-vquery.ts | 2 +- .../adapters/vquery-vseed/build-vseed.ts | 4 +- .../adapters/vquery-vseed/index.ts | 0 .../adapters/vquery-vseed/types.ts | 0 .../src/{builder => chart-builder}/builder.ts | 8 +- .../{builder => chart-builder}/connector.ts | 0 .../features/chart-type/chart-type-builder.ts | 0 .../features/chart-type/dimension-encoding.ts | 0 .../features/chart-type/index.ts | 0 .../features/chart-type/measure-encoding.ts | 0 .../chart-type/reapply-dimension-encodings.ts | 0 .../chart-type/reapply-measure-encodings.ts | 0 .../features/dimensions/dim-builder.ts | 0 .../features/dimensions/dim-node-builder.ts | 2 +- .../features/dimensions/dimension-utils.ts | 0 .../features/dimensions/index.ts | 0 .../features/havingFilter/having-builder.ts | 0 .../havingFilter/having-group-builder.ts | 0 .../havingFilter/having-node-builder.ts | 2 +- .../features/havingFilter/having-utils.ts | 0 .../features/havingFilter/index.ts | 0 .../features/index.ts | 0 .../features/limit/index.ts | 0 .../features/limit/limit-builder.ts | 0 .../features/locale/index.ts | 0 .../features/locale/locale-builder.ts | 0 .../features/measures/index.ts | 0 .../features/measures/mea-builder.ts | 0 .../features/measures/mea-node-builder.ts | 2 +- .../features/measures/measure-utils.ts | 0 .../features/theme/index.ts | 0 .../features/theme/theme-builder.ts | 0 .../features/undo-manager/index.ts | 0 .../features/undo-manager/undo-manager.ts | 0 .../features/whereFilter/index.ts | 0 .../features/whereFilter/where-builder.ts | 0 .../whereFilter/where-group-builder.ts | 0 .../whereFilter/where-node-builder.ts | 2 +- .../features/whereFilter/where-utils.ts | 0 .../src/{builder => chart-builder}/index.ts | 5 +- .../modules/apply-update.ts | 0 .../modules/build.ts | 0 .../modules/encode-state-as-update.ts | 0 .../modules/get-schema.ts | 0 .../modules/index.ts | 0 .../modules/is-empty.ts | 0 .../src/{ => chart-builder}/pipeline/index.ts | 0 .../pipeline/vqueryDSL/aggregateMap.ts | 0 .../pipeline/vqueryDSL/buildGroupBy.ts | 2 +- .../pipeline/vqueryDSL/buildHaving.ts | 2 +- .../pipeline/vqueryDSL/buildLimit.ts | 0 .../pipeline/vqueryDSL/buildOrderBy.ts | 2 +- .../pipeline/vqueryDSL/buildSelect.ts | 2 +- .../pipeline/vqueryDSL/buildWhere.ts | 2 +- .../pipeline/vqueryDSL/index.ts | 2 +- .../vqueryDSL/resolveDatePredicate.ts | 2 +- .../pipeline/vqueryDSL/types.ts | 2 +- packages/vbi/src/index.ts | 9 +- packages/vbi/src/report-builder/builder.ts | 42 ++ .../vbi/src/report-builder/features/index.ts | 2 + .../src/report-builder/features/page/index.ts | 3 + .../features/page/page-builder.ts | 51 +++ .../features/page/page-collection-builder.ts | 75 ++++ .../features/page/text-builder.ts | 24 ++ packages/vbi/src/report-builder/index.ts | 2 + .../vbi/src/report-builder/modules/build.ts | 7 + .../vbi/src/report-builder/modules/index.ts | 4 + .../src/report-builder/modules/is-empty.ts | 6 + .../vbi/src/types/builder/VBIInterface.ts | 6 +- packages/vbi/src/types/builder/adapter.ts | 4 +- packages/vbi/src/types/builder/context.ts | 2 +- packages/vbi/src/types/builder/index.ts | 1 + packages/vbi/src/types/builder/report.ts | 23 ++ .../{dsl => chartDSL}/dimensions/aggregate.ts | 0 .../dimensions/dimensions.ts | 0 .../src/types/{dsl => chartDSL}/encoding.ts | 0 .../{dsl => chartDSL}/havingFilter/having.ts | 0 .../vbi/src/types/{dsl => chartDSL}/index.ts | 0 .../types/{dsl => chartDSL}/locale/locale.ts | 0 .../{dsl => chartDSL}/measures/aggregate.ts | 0 .../{dsl => chartDSL}/measures/measures.ts | 0 .../vbi/src/types/{dsl => chartDSL}/sort.ts | 0 .../types/{dsl => chartDSL}/theme/theme.ts | 0 .../src/types/{dsl => chartDSL}/vbi/vbi.ts | 0 .../{dsl => chartDSL}/whereFilter/date.ts | 0 .../{dsl => chartDSL}/whereFilter/filters.ts | 0 packages/vbi/src/types/index.ts | 3 +- packages/vbi/src/types/reportDSL/index.ts | 3 + packages/vbi/src/types/reportDSL/page.ts | 13 + packages/vbi/src/types/reportDSL/report.ts | 10 + packages/vbi/src/types/reportDSL/text.ts | 8 + packages/vbi/src/utils/filter-guards.ts | 2 +- packages/vbi/src/vbi/create-vbi.ts | 35 +- .../src/vbi/from/fill-vbi-chart-dsl-map.ts | 15 + .../vbi/src/vbi/from/from-vbi-dsl-input.ts | 17 +- .../src/vbi/from/from-vbi-report-dsl-input.ts | 22 ++ .../vbi/src/vbi/from/report-page-y-map.ts | 69 ++++ .../vbi/src/vbi/from/set-base-dsl-fields.ts | 10 +- .../vbi/src/vbi/generate-empty-report-dsl.ts | 8 + .../src/vbi/generate-empty-report-page-dsl.ts | 15 + .../src/vbi/normalize/ensure-having-group.ts | 2 +- .../src/vbi/normalize/ensure-where-group.ts | 2 +- packages/vbi/tests/builder/builder.test.ts | 4 +- .../tests/builder/features/chartType.test.ts | 2 +- .../builder/features/configFeatures.test.ts | 22 +- .../tests/builder/features/dimensions.test.ts | 4 +- .../builder/features/filterCoverage.test.ts | 10 +- .../builder/features/havingFilter.test.ts | 8 +- .../tests/builder/features/measures.test.ts | 4 +- .../vbi/tests/builder/features/sort.test.ts | 2 +- .../builder/features/whereFilter.test.ts | 2 +- packages/vbi/tests/builder/filters.test.ts | 2 +- .../vbi/tests/builder/reportBuilder.test.ts | 109 +++++ .../vbi/tests/builder/undo-manager.test.ts | 2 +- packages/vbi/tests/builder/yjs.test.ts | 2 +- .../examples/whereFilter/whereFilter.test.ts | 4 +- packages/vbi/tests/query/orderBy.test.ts | 2 +- .../vbi/tests/types/dateFilterSchemas.test.ts | 2 +- .../vbi/tests/types/reportSchemas.test.ts | 59 +++ .../vbi/tests/types/runtimeSchemas.test.ts | 18 +- packages/vbi/tests/types/sortSchemas.test.ts | 8 +- 159 files changed, 1628 insertions(+), 364 deletions(-) create mode 100644 apps/website/docs/zh-CN/vbi/api/chartBuilder/_meta.json rename apps/website/docs/zh-CN/vbi/api/{chart-type.md => chartBuilder/chartType.md} (99%) create mode 100644 apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/_meta.json rename apps/website/docs/zh-CN/vbi/api/{dimensions/dimension-node.md => chartBuilder/dimensions/dimensionNode.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{dimensions.md => chartBuilder/dimensions/index.md} (99%) create mode 100644 apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/_meta.json rename apps/website/docs/zh-CN/vbi/api/{having-filter/having-group.md => chartBuilder/havingFilter/havingGroup.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{having-filter/having-node.md => chartBuilder/havingFilter/havingNode.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{having-filter.md => chartBuilder/havingFilter/index.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{builder.md => chartBuilder/index.md} (97%) rename apps/website/docs/zh-CN/vbi/api/{ => chartBuilder}/limit.md (98%) rename apps/website/docs/zh-CN/vbi/api/{ => chartBuilder}/locale.md (98%) create mode 100644 apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/_meta.json rename apps/website/docs/zh-CN/vbi/api/{measures.md => chartBuilder/measures/index.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{measures/measure-node.md => chartBuilder/measures/measureNode.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{ => chartBuilder}/theme.md (98%) rename apps/website/docs/zh-CN/vbi/api/{undo-manager.md => chartBuilder/undoManager.md} (98%) create mode 100644 apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/_meta.json rename apps/website/docs/zh-CN/vbi/api/{where-filter.md => chartBuilder/whereFilter/index.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{where-filter/where-group.md => chartBuilder/whereFilter/whereGroup.md} (99%) rename apps/website/docs/zh-CN/vbi/api/{where-filter/where-node.md => chartBuilder/whereFilter/whereNode.md} (98%) delete mode 100644 apps/website/docs/zh-CN/vbi/api/dimensions/_meta.json delete mode 100644 apps/website/docs/zh-CN/vbi/api/having-filter/_meta.json delete mode 100644 apps/website/docs/zh-CN/vbi/api/measures/_meta.json create mode 100644 apps/website/docs/zh-CN/vbi/api/reportBuilder/_meta.json create mode 100644 apps/website/docs/zh-CN/vbi/api/reportBuilder/index.md create mode 100644 apps/website/docs/zh-CN/vbi/api/reportBuilder/page/_meta.json create mode 100644 apps/website/docs/zh-CN/vbi/api/reportBuilder/page/index.md create mode 100644 apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportPage.md create mode 100644 apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportText.md delete mode 100644 apps/website/docs/zh-CN/vbi/api/where-filter/_meta.json create mode 100644 packages/vbi/docs/todo3-create-report/adr.md create mode 100644 packages/vbi/docs/todo3-create-report/plan.md rename packages/vbi/src/{builder => chart-builder}/adapters/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/adapters/vquery-vseed/build-vquery.ts (75%) rename packages/vbi/src/{builder => chart-builder}/adapters/vquery-vseed/build-vseed.ts (93%) rename packages/vbi/src/{builder => chart-builder}/adapters/vquery-vseed/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/adapters/vquery-vseed/types.ts (100%) rename packages/vbi/src/{builder => chart-builder}/builder.ts (92%) rename packages/vbi/src/{builder => chart-builder}/connector.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/chart-type/chart-type-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/chart-type/dimension-encoding.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/chart-type/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/chart-type/measure-encoding.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/chart-type/reapply-dimension-encodings.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/chart-type/reapply-measure-encodings.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/dimensions/dim-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/dimensions/dim-node-builder.ts (96%) rename packages/vbi/src/{builder => chart-builder}/features/dimensions/dimension-utils.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/dimensions/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/havingFilter/having-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/havingFilter/having-group-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/havingFilter/having-node-builder.ts (95%) rename packages/vbi/src/{builder => chart-builder}/features/havingFilter/having-utils.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/havingFilter/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/limit/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/limit/limit-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/locale/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/locale/locale-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/measures/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/measures/mea-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/measures/mea-node-builder.ts (96%) rename packages/vbi/src/{builder => chart-builder}/features/measures/measure-utils.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/theme/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/theme/theme-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/undo-manager/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/undo-manager/undo-manager.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/whereFilter/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/whereFilter/where-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/whereFilter/where-group-builder.ts (100%) rename packages/vbi/src/{builder => chart-builder}/features/whereFilter/where-node-builder.ts (95%) rename packages/vbi/src/{builder => chart-builder}/features/whereFilter/where-utils.ts (100%) rename packages/vbi/src/{builder => chart-builder}/index.ts (81%) rename packages/vbi/src/{builder => chart-builder}/modules/apply-update.ts (100%) rename packages/vbi/src/{builder => chart-builder}/modules/build.ts (100%) rename packages/vbi/src/{builder => chart-builder}/modules/encode-state-as-update.ts (100%) rename packages/vbi/src/{builder => chart-builder}/modules/get-schema.ts (100%) rename packages/vbi/src/{builder => chart-builder}/modules/index.ts (100%) rename packages/vbi/src/{builder => chart-builder}/modules/is-empty.ts (100%) rename packages/vbi/src/{ => chart-builder}/pipeline/index.ts (100%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/aggregateMap.ts (100%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/buildGroupBy.ts (90%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/buildHaving.ts (98%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/buildLimit.ts (100%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/buildOrderBy.ts (93%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/buildSelect.ts (94%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/buildWhere.ts (98%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/index.ts (91%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/resolveDatePredicate.ts (99%) rename packages/vbi/src/{ => chart-builder}/pipeline/vqueryDSL/types.ts (76%) create mode 100644 packages/vbi/src/report-builder/builder.ts create mode 100644 packages/vbi/src/report-builder/features/index.ts create mode 100644 packages/vbi/src/report-builder/features/page/index.ts create mode 100644 packages/vbi/src/report-builder/features/page/page-builder.ts create mode 100644 packages/vbi/src/report-builder/features/page/page-collection-builder.ts create mode 100644 packages/vbi/src/report-builder/features/page/text-builder.ts create mode 100644 packages/vbi/src/report-builder/index.ts create mode 100644 packages/vbi/src/report-builder/modules/build.ts create mode 100644 packages/vbi/src/report-builder/modules/index.ts create mode 100644 packages/vbi/src/report-builder/modules/is-empty.ts create mode 100644 packages/vbi/src/types/builder/report.ts rename packages/vbi/src/types/{dsl => chartDSL}/dimensions/aggregate.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/dimensions/dimensions.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/encoding.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/havingFilter/having.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/index.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/locale/locale.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/measures/aggregate.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/measures/measures.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/sort.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/theme/theme.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/vbi/vbi.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/whereFilter/date.ts (100%) rename packages/vbi/src/types/{dsl => chartDSL}/whereFilter/filters.ts (100%) create mode 100644 packages/vbi/src/types/reportDSL/index.ts create mode 100644 packages/vbi/src/types/reportDSL/page.ts create mode 100644 packages/vbi/src/types/reportDSL/report.ts create mode 100644 packages/vbi/src/types/reportDSL/text.ts create mode 100644 packages/vbi/src/vbi/from/fill-vbi-chart-dsl-map.ts create mode 100644 packages/vbi/src/vbi/from/from-vbi-report-dsl-input.ts create mode 100644 packages/vbi/src/vbi/from/report-page-y-map.ts create mode 100644 packages/vbi/src/vbi/generate-empty-report-dsl.ts create mode 100644 packages/vbi/src/vbi/generate-empty-report-page-dsl.ts create mode 100644 packages/vbi/tests/builder/reportBuilder.test.ts create mode 100644 packages/vbi/tests/types/reportSchemas.test.ts diff --git a/apps/website/docs/zh-CN/vbi/api/_meta.json b/apps/website/docs/zh-CN/vbi/api/_meta.json index d676348411..e6bf9dbd1b 100644 --- a/apps/website/docs/zh-CN/vbi/api/_meta.json +++ b/apps/website/docs/zh-CN/vbi/api/_meta.json @@ -1,61 +1,21 @@ [ { "type": "file", - "name": "builder", - "label": "builder" - }, - { - "type": "file", - "name": "chart-type", - "label": "builder.chartType", - "collapsed": true - }, - { - "type": "dir", - "name": "measures", - "label": "builder.measures", - "collapsed": true + "name": "index", + "label": "API" }, { "type": "dir", - "name": "dimensions", - "label": "builder.dimensions", - "collapsed": true + "name": "chartBuilder", + "label": "chartBuilder", + "collapsible": true, + "collapsed": false }, { "type": "dir", - "name": "where-filter", - "label": "builder.whereFilter", - "collapsed": true - }, - { - "type": "dir", - "name": "having-filter", - "label": "builder.havingFilter", - "collapsed": true - }, - { - "type": "file", - "name": "theme", - "label": "builder.theme", - "collapsed": true - }, - { - "type": "file", - "name": "locale", - "label": "builder.locale", - "collapsed": true - }, - { - "type": "file", - "name": "limit", - "label": "builder.limit", - "collapsed": true - }, - { - "type": "file", - "name": "undo-manager", - "label": "builder.undoManager", - "collapsed": true + "name": "reportBuilder", + "label": "reportBuilder", + "collapsible": true, + "collapsed": false } ] diff --git a/apps/website/docs/zh-CN/vbi/api/chartBuilder/_meta.json b/apps/website/docs/zh-CN/vbi/api/chartBuilder/_meta.json new file mode 100644 index 0000000000..a62ed36469 --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/_meta.json @@ -0,0 +1,55 @@ +[ + { + "type": "file", + "name": "chartType", + "label": "chartBuilder.chartType" + }, + { + "type": "dir", + "name": "measures", + "label": "chartBuilder.measures", + "collapsible": true, + "collapsed": true + }, + { + "type": "dir", + "name": "dimensions", + "label": "chartBuilder.dimensions", + "collapsible": true, + "collapsed": true + }, + { + "type": "dir", + "name": "whereFilter", + "label": "chartBuilder.whereFilter", + "collapsible": true, + "collapsed": true + }, + { + "type": "dir", + "name": "havingFilter", + "label": "chartBuilder.havingFilter", + "collapsible": true, + "collapsed": true + }, + { + "type": "file", + "name": "theme", + "label": "chartBuilder.theme" + }, + { + "type": "file", + "name": "locale", + "label": "chartBuilder.locale" + }, + { + "type": "file", + "name": "limit", + "label": "chartBuilder.limit" + }, + { + "type": "file", + "name": "undoManager", + "label": "chartBuilder.undoManager" + } +] diff --git a/apps/website/docs/zh-CN/vbi/api/chart-type.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/chartType.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/chart-type.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/chartType.md index 360ef34c35..203ba3b13e 100644 --- a/apps/website/docs/zh-CN/vbi/api/chart-type.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/chartType.md @@ -1,4 +1,4 @@ -# chartType +# ChartTypeBuilder 图表类型构建器,用于切换和获取图表类型。支持表格、柱状图、折线图、饼图、散点图等多种图表类型 diff --git a/apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/_meta.json b/apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/_meta.json new file mode 100644 index 0000000000..b2935e2dae --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/_meta.json @@ -0,0 +1,7 @@ +[ + { + "type": "file", + "name": "dimensionNode", + "label": "dimensionNode" + } +] diff --git a/apps/website/docs/zh-CN/vbi/api/dimensions/dimension-node.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/dimensionNode.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/dimensions/dimension-node.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/dimensionNode.md index 15695b8041..92cd8ec671 100644 --- a/apps/website/docs/zh-CN/vbi/api/dimensions/dimension-node.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/dimensionNode.md @@ -2,6 +2,8 @@ 维度节点构建器,用于配置单个维度 +## 属性 + ## 方法 ### constructor diff --git a/apps/website/docs/zh-CN/vbi/api/dimensions.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/index.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/dimensions.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/index.md index 86221e4d57..e0c17ad0a3 100644 --- a/apps/website/docs/zh-CN/vbi/api/dimensions.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/dimensions/index.md @@ -1,4 +1,4 @@ -# dimensions +# DimensionsBuilder 维度构建器,用于添加、修改、删除维度配置。维度是数据的分类字段,如:时间、地区、产品类别 diff --git a/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/_meta.json b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/_meta.json new file mode 100644 index 0000000000..53d6c9dd5d --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/_meta.json @@ -0,0 +1,12 @@ +[ + { + "type": "file", + "name": "havingNode", + "label": "havingNode" + }, + { + "type": "file", + "name": "havingGroup", + "label": "havingGroup" + } +] diff --git a/apps/website/docs/zh-CN/vbi/api/having-filter/having-group.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/havingGroup.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/having-filter/having-group.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/havingGroup.md index a00bb88797..51b9ea4175 100644 --- a/apps/website/docs/zh-CN/vbi/api/having-filter/having-group.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/havingGroup.md @@ -2,6 +2,8 @@ Having 分组构建器,用于配置一组条件的逻辑关系(AND/OR) +## 属性 + ## 方法 ### constructor diff --git a/apps/website/docs/zh-CN/vbi/api/having-filter/having-node.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/havingNode.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/having-filter/having-node.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/havingNode.md index e9f6e17fd3..b8a8560cab 100644 --- a/apps/website/docs/zh-CN/vbi/api/having-filter/having-node.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/havingNode.md @@ -2,6 +2,8 @@ Having 过滤节点构建器,用于配置单个 Having 过滤条件 +## 属性 + ## 方法 ### constructor diff --git a/apps/website/docs/zh-CN/vbi/api/having-filter.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/index.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/having-filter.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/index.md index c5825a6fd9..2c2f0a6cc0 100644 --- a/apps/website/docs/zh-CN/vbi/api/having-filter.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/havingFilter/index.md @@ -1,4 +1,4 @@ -# havingFilter +# HavingFilterBuilder Having 过滤构建器,用于添加、修改、删除分组后过滤条件。Having 过滤在数据聚合后生效,用于筛选分组结果 diff --git a/apps/website/docs/zh-CN/vbi/api/builder.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/index.md similarity index 97% rename from apps/website/docs/zh-CN/vbi/api/builder.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/index.md index bbfbaf2658..98b6b678f8 100644 --- a/apps/website/docs/zh-CN/vbi/api/builder.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/index.md @@ -25,7 +25,7 @@ **定义**: ```typescript -constructor(doc: Y.Doc, options: VBIChartBuilderOptions) +constructor(doc: Y.Doc, options: VBIChartBuilderOptions, dsl: Y.Map) ``` **参数**: @@ -34,6 +34,7 @@ constructor(doc: Y.Doc, options: VBIChartBuilderOptions) | --- | --- | --- | | `doc` | Y.Doc | - | | `options` | VBIChartBuilderOptions | - | +| `dsl` | Y.Map | - | ### applyUpdate diff --git a/apps/website/docs/zh-CN/vbi/api/limit.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/limit.md similarity index 98% rename from apps/website/docs/zh-CN/vbi/api/limit.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/limit.md index 13e1255b63..5375c6c57f 100644 --- a/apps/website/docs/zh-CN/vbi/api/limit.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/limit.md @@ -1,4 +1,4 @@ -# limit +# LimitBuilder 数据量限制构建器,用于设置和获取当前 limit diff --git a/apps/website/docs/zh-CN/vbi/api/locale.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/locale.md similarity index 98% rename from apps/website/docs/zh-CN/vbi/api/locale.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/locale.md index 8da6a04d0a..4c8a5d5950 100644 --- a/apps/website/docs/zh-CN/vbi/api/locale.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/locale.md @@ -1,4 +1,4 @@ -# locale +# LocaleBuilder 语言构建器,用于设置和获取当前语言 diff --git a/apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/_meta.json b/apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/_meta.json new file mode 100644 index 0000000000..e0b2e41c98 --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/_meta.json @@ -0,0 +1,7 @@ +[ + { + "type": "file", + "name": "measureNode", + "label": "measureNode" + } +] diff --git a/apps/website/docs/zh-CN/vbi/api/measures.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/index.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/measures.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/index.md index 8e2dff8c70..b11c339ac1 100644 --- a/apps/website/docs/zh-CN/vbi/api/measures.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/index.md @@ -1,4 +1,4 @@ -# measures +# MeasuresBuilder 度量构建器,用于添加、修改、删除度量配置。度量是数据的数值字段,如:销售额、利润、数量 diff --git a/apps/website/docs/zh-CN/vbi/api/measures/measure-node.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/measureNode.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/measures/measure-node.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/measureNode.md index d62a6f32e4..50e474abaa 100644 --- a/apps/website/docs/zh-CN/vbi/api/measures/measure-node.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/measures/measureNode.md @@ -2,6 +2,8 @@ 度量节点构建器,用于配置单个度量 +## 属性 + ## 方法 ### constructor diff --git a/apps/website/docs/zh-CN/vbi/api/theme.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/theme.md similarity index 98% rename from apps/website/docs/zh-CN/vbi/api/theme.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/theme.md index be52b89cbb..3b42c53f6b 100644 --- a/apps/website/docs/zh-CN/vbi/api/theme.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/theme.md @@ -1,4 +1,4 @@ -# theme +# ThemeBuilder 主题构建器,用于设置和获取当前主题 diff --git a/apps/website/docs/zh-CN/vbi/api/undo-manager.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/undoManager.md similarity index 98% rename from apps/website/docs/zh-CN/vbi/api/undo-manager.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/undoManager.md index 8f0bb958dd..db0e4dd362 100644 --- a/apps/website/docs/zh-CN/vbi/api/undo-manager.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/undoManager.md @@ -1,4 +1,4 @@ -# undoManager +# UndoManager 撤销/重做管理器,提供基于 YJS 的撤销和重做功能,支持栈管理和历史清除操作 diff --git a/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/_meta.json b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/_meta.json new file mode 100644 index 0000000000..b64faeca92 --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/_meta.json @@ -0,0 +1,12 @@ +[ + { + "type": "file", + "name": "whereNode", + "label": "whereNode" + }, + { + "type": "file", + "name": "whereGroup", + "label": "whereGroup" + } +] diff --git a/apps/website/docs/zh-CN/vbi/api/where-filter.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/index.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/where-filter.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/index.md index 84b76fad3d..3de386d1df 100644 --- a/apps/website/docs/zh-CN/vbi/api/where-filter.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/index.md @@ -1,4 +1,4 @@ -# whereFilter +# WhereFilterBuilder Where 过滤构建器,用于添加、修改、删除行级过滤条件。Where 过滤在数据查询前生效,用于筛选原始数据 diff --git a/apps/website/docs/zh-CN/vbi/api/where-filter/where-group.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/whereGroup.md similarity index 99% rename from apps/website/docs/zh-CN/vbi/api/where-filter/where-group.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/whereGroup.md index 10b91a6780..ec20bd463d 100644 --- a/apps/website/docs/zh-CN/vbi/api/where-filter/where-group.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/whereGroup.md @@ -2,6 +2,8 @@ Where 分组构建器,用于配置一组条件的逻辑关系(AND/OR) +## 属性 + ## 方法 ### constructor diff --git a/apps/website/docs/zh-CN/vbi/api/where-filter/where-node.md b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/whereNode.md similarity index 98% rename from apps/website/docs/zh-CN/vbi/api/where-filter/where-node.md rename to apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/whereNode.md index 8209726127..d59a09fc1e 100644 --- a/apps/website/docs/zh-CN/vbi/api/where-filter/where-node.md +++ b/apps/website/docs/zh-CN/vbi/api/chartBuilder/whereFilter/whereNode.md @@ -1,7 +1,9 @@ -# WhereNodeBuilder +# WhereFilterNodeBuilder Where 过滤节点构建器,用于配置单个 Where 过滤条件 +## 属性 + ## 方法 ### constructor diff --git a/apps/website/docs/zh-CN/vbi/api/dimensions/_meta.json b/apps/website/docs/zh-CN/vbi/api/dimensions/_meta.json deleted file mode 100644 index 2ebc47c154..0000000000 --- a/apps/website/docs/zh-CN/vbi/api/dimensions/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "type": "file", - "name": "dimension-node", - "label": "dimension-node", - "collapsed": true - } -] diff --git a/apps/website/docs/zh-CN/vbi/api/having-filter/_meta.json b/apps/website/docs/zh-CN/vbi/api/having-filter/_meta.json deleted file mode 100644 index 43797f8ebd..0000000000 --- a/apps/website/docs/zh-CN/vbi/api/having-filter/_meta.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "type": "file", - "name": "having-node", - "label": "having-node", - "collapsed": true - }, - { - "type": "file", - "name": "having-group", - "label": "having-group", - "collapsed": true - } -] diff --git a/apps/website/docs/zh-CN/vbi/api/index.md b/apps/website/docs/zh-CN/vbi/api/index.md index 41fc3a5f6d..1c97e50075 100644 --- a/apps/website/docs/zh-CN/vbi/api/index.md +++ b/apps/website/docs/zh-CN/vbi/api/index.md @@ -1,3 +1,4 @@ --- overview: true +title: API --- diff --git a/apps/website/docs/zh-CN/vbi/api/measures/_meta.json b/apps/website/docs/zh-CN/vbi/api/measures/_meta.json deleted file mode 100644 index c698d8794a..0000000000 --- a/apps/website/docs/zh-CN/vbi/api/measures/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "type": "file", - "name": "measure-node", - "label": "measure-node", - "collapsed": true - } -] diff --git a/apps/website/docs/zh-CN/vbi/api/reportBuilder/_meta.json b/apps/website/docs/zh-CN/vbi/api/reportBuilder/_meta.json new file mode 100644 index 0000000000..6f8413591c --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/reportBuilder/_meta.json @@ -0,0 +1,9 @@ +[ + { + "type": "dir", + "name": "page", + "label": "reportBuilder.page", + "collapsible": true, + "collapsed": true + } +] diff --git a/apps/website/docs/zh-CN/vbi/api/reportBuilder/index.md b/apps/website/docs/zh-CN/vbi/api/reportBuilder/index.md new file mode 100644 index 0000000000..8ad31032ac --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/reportBuilder/index.md @@ -0,0 +1,81 @@ +# VBIReportBuilder + +## 属性 + +| 属性 | 类型 | 说明 | +| --- | --- | --- | +| **doc** | `Y.Doc` | - | +| **dsl** | `Y.Map` | - | +| **undoManager** | `UndoManager` | - | +| **page** | `ReportPageCollectionBuilder` | - | + + +## 方法 + +### constructor + +**定义**: + +```typescript +constructor(doc: Y.Doc, options: VBIReportBuilderOptions) +``` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `doc` | Y.Doc | - | +| `options` | VBIReportBuilderOptions | - | + +### applyUpdate + +**定义**: + +```typescript +applyUpdate(update: Uint8Array, transactionOrigin: any): any +``` + +**返回**: `any` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `update` | Uint8Array | - | +| `transactionOrigin` | any | - | + +### encodeStateAsUpdate + +**定义**: + +```typescript +encodeStateAsUpdate(targetStateVector: Uint8Array): any +``` + +**返回**: `any` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `targetStateVector` | Uint8Array | - | + +### build + +**定义**: + +```typescript +build(): VBIReportDSL +``` + +**返回**: `VBIReportDSL` + +### isEmpty + +**定义**: + +```typescript +isEmpty(): boolean +``` + +**返回**: `boolean` \ No newline at end of file diff --git a/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/_meta.json b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/_meta.json new file mode 100644 index 0000000000..73e9b2937a --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/_meta.json @@ -0,0 +1,12 @@ +[ + { + "type": "file", + "name": "reportPage", + "label": "reportPage" + }, + { + "type": "file", + "name": "reportText", + "label": "reportText" + } +] diff --git a/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/index.md b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/index.md new file mode 100644 index 0000000000..610c781ad8 --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/index.md @@ -0,0 +1,88 @@ +# ReportPageCollectionBuilder + +## 属性 + +## 方法 + +### constructor + +**定义**: + +```typescript +constructor(parent: VBIReportBuilder, doc: Y.Doc, dsl: Y.Map, options: VBIReportBuilderOptions) +``` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `parent` | VBIReportBuilder | - | +| `doc` | Y.Doc | - | +| `dsl` | Y.Map | - | +| `options` | VBIReportBuilderOptions | - | + +### add + +**定义**: + +```typescript +add(title: string, callback: (page: ReportPageBuilder) => void): VBIReportBuilder +``` + +**返回**: `VBIReportBuilder` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `title` | string | - | +| `callback` | (page: ReportPageBuilder) => void | - | + +### remove + +**定义**: + +```typescript +remove(pageId: string): VBIReportBuilder +``` + +**返回**: `VBIReportBuilder` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `pageId` | string | - | + +### update + +**定义**: + +```typescript +update(pageId: string, callback: (page: ReportPageBuilder) => void): VBIReportBuilder +``` + +**返回**: `VBIReportBuilder` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `pageId` | string | - | +| `callback` | (page: ReportPageBuilder) => void | - | + +### get + +**定义**: + +```typescript +get(pageId: string): ReportPageBuilder | undefined +``` + +**返回**: `ReportPageBuilder \| undefined` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `pageId` | string | - | \ No newline at end of file diff --git a/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportPage.md b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportPage.md new file mode 100644 index 0000000000..aef2b1eb60 --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportPage.md @@ -0,0 +1,95 @@ +# ReportPageBuilder + +## 属性 + +| 属性 | 类型 | 说明 | +| --- | --- | --- | +| **chart** | `VBIChartBuilder` | - | +| **text** | `ReportTextBuilder` | - | + + +## 方法 + +### constructor + +**定义**: + +```typescript +constructor(doc: Y.Doc, page: Y.Map, chartOptions: VBIChartBuilderOptions) +``` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `doc` | Y.Doc | - | +| `page` | Y.Map | - | +| `chartOptions` | VBIChartBuilderOptions | - | + +### getId + +**定义**: + +```typescript +getId(): string +``` + +**返回**: `string` + +### setTitle + +**定义**: + +```typescript +setTitle(title: string): this +``` + +**返回**: `this` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `title` | string | - | + +### setChart + +**定义**: + +```typescript +setChart(chartBuilder: VBIChartBuilder | VBIChartDSLInput): this +``` + +**返回**: `this` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `chartBuilder` | VBIChartBuilder \| VBIChartDSLInput | - | + +### setText + +**定义**: + +```typescript +setText(content: string): this +``` + +**返回**: `this` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `content` | string | - | + +### toJSON + +**定义**: + +```typescript +toJSON(): VBIReportPageDSL +``` + +**返回**: `VBIReportPageDSL` \ No newline at end of file diff --git a/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportText.md b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportText.md new file mode 100644 index 0000000000..49c038220d --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/api/reportBuilder/page/reportText.md @@ -0,0 +1,65 @@ +# ReportTextBuilder + +## 属性 + +## 方法 + +### constructor + +**定义**: + +```typescript +constructor(yMap: Y.Map) +``` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `yMap` | Y.Map | - | + +### getContent + +**定义**: + +```typescript +getContent(): string +``` + +**返回**: `string` + +### setContent + +**定义**: + +```typescript +setContent(content: string): this +``` + +**返回**: `this` + +**参数**: + +| 参数 | 类型 | 说明 | +| --- | --- | --- | +| `content` | string | - | + +### clear + +**定义**: + +```typescript +clear(): this +``` + +**返回**: `this` + +### toJSON + +**定义**: + +```typescript +toJSON(): VBIReportTextDSL +``` + +**返回**: `VBIReportTextDSL` \ No newline at end of file diff --git a/apps/website/docs/zh-CN/vbi/api/where-filter/_meta.json b/apps/website/docs/zh-CN/vbi/api/where-filter/_meta.json deleted file mode 100644 index 1dcf71bfbc..0000000000 --- a/apps/website/docs/zh-CN/vbi/api/where-filter/_meta.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "type": "file", - "name": "where-node", - "label": "where-node", - "collapsed": true - }, - { - "type": "file", - "name": "where-group", - "label": "where-group", - "collapsed": true - } -] diff --git a/packages/vbi/docs/todo3-create-report/adr.md b/packages/vbi/docs/todo3-create-report/adr.md new file mode 100644 index 0000000000..4dcbe32473 --- /dev/null +++ b/packages/vbi/docs/todo3-create-report/adr.md @@ -0,0 +1,116 @@ +# ADR-006: VBI Report DSL 与 ReportBuilder + +## Context + +`ADR-005` 已把单图表能力收敛为 `createChart`、`VBIChartBuilder`、`VBIChartDSL`,现在可以在不混淆命名的前提下引入 report。 + +本任务只解决 `packages/vbi` 内的 report 建模与 builder 设计: + +1. 新增 `VBI.createReport(...)`,让 report 成为和 chart 平级的一等入口。 +2. 一个 `report` 包含多个 `page`,一个 `page` 固定包含一个 `chart` 和一个 `text`。 +3. `VBIReportDSL`、zod schema、空 DSL helper 的风格与 `VBIChartDSL` 保持一致。 +4. `reportBuilder` 的使用、实现、协同编辑方式尽量复用 `chartBuilder`。 + +这里有三个直接风险: + +1. report 根节点重复定义 chart 字段,会破坏 Single Source of Truth。 +2. 一开始就抽象成通用 widgets/layout system,会明显超出当前需求。 +3. report 另起一套 chart 编辑逻辑,chart/report 很快会发生行为漂移。 + +## Decision + +### 1. 根入口统一为 `createReport(...)` + +新增并文档化以下入口: + +```ts +VBI.createReport(vbiReport, options) +createVBI(...).createReport(vbiReport, options) +``` + +对外命名统一为 `VBIReportDSL` / `VBIReportDSLInput` / `zVBIReportDSL` / `generateEmptyReportDSL` / `generateEmptyReportPageDSL` / `VBIReportBuilder` / `VBIReportBuilderInterface` / `VBIReportBuilderOptions`。`chart` 和 `report` 是 `VBI` 下的两个平级能力。 + +### 2. `types/dsl` 改名为 `types/chartDSL`,并与 `types/reportDSL` 平级 + +当前 `types/dsl` 实际承载的是 chart 领域模型,不适合在引入 report 后继续占用泛化名字。目录应调整为: + +1. `types/chartDSL/*`:现有 `VBIChartDSL` 及其子节点 schema。 +2. `types/reportDSL/*`:新增 `VBIReportDSL`、`VBIReportPageDSL`、`VBIReportTextDSL`。 +3. `types/index.ts` 只负责聚合导出,不再让 `chart` 和 `report` 共用 `dsl` 这个模糊目录名。 + +### 3. Report DSL 采用固定 page 结构,不做通用 block union + +首期 DSL 固定为: + +```ts +type VBIReportTextDSL = { content: string } +type VBIReportPageDSL = { id: string; title: string; chart: VBIChartDSL; text: VBIReportTextDSL } +type VBIReportDSL = { pages: VBIReportPageDSL[]; version: number } +``` + +约束如下: + +1. `pages` 必须是数组,因为页面顺序本身就是业务语义。 +2. `page.title` 是 page 的显示名,`page.add('Story One', ...)` 的第一个参数直接写入这里。 +3. `page.chart` 直接内嵌 `VBIChartDSL`,它是图表配置的唯一事实来源。 +4. `page.text` 先保持极简对象 `{ content }`,不在首期引入 rich-text schema。 +5. `report` 根节点不新增 `connectorId`,避免和 `page.chart.connectorId` 双写。 + +### 4. 默认值与空 DSL helper 保持 chart 风格 + +首期 helper 约定如下: + +```ts +generateEmptyReportDSL() => { pages: [], version: 0 } +generateEmptyReportPageDSL(connectorId) => ({ id, title: '', chart: generateEmptyChartDSL(connectorId), text: { content: '' } }) +``` + +`zVBIReportDSL` 与 `zVBIReportPageDSL` 也应使用同风格默认值,让 `build()` 产物保持稳定、最小、可预测。 + +### 5. `VBIReportBuilder` 提供 `reportBuilder.page.*`;图表 lowering 仍由 page.chart 负责 + +首期 builder 结构固定为: + +```ts +class VBIReportBuilder { + page: ReportPageCollectionBuilder + build(): VBIReportDSL + isEmpty(): boolean + applyUpdate(...) + encodeStateAsUpdate(...) +} +class ReportPageBuilder { + setChart(chartBuilder: VBIChartBuilder): this + setText(content: string): this +} +``` + +其中: + +1. `reportBuilder.page.add(title, callback)` / `remove(id)` / `update(id, callback)` 是主入口,`add` 与 `update` 都返回 reportBuilder 以便链式调用。 +2. 推荐用法固定为 `reportBuilder.page.add('Story One', page => page.setChart(chartBuilder).setText('hello world'))`。 +3. `setChart(chartBuilder)` 会把传入 `chartBuilder.build()` 的结果复制到当前 page 的 `chart` 子树,而不是共享同一个 builder 实例。 +4. `VBIReportBuilder` 不提供 report 级 `buildVQuery()` / `buildVSeed()`;这些能力仍属于 `page.chart`。 + +### 6. 实现上复用同一套 chart builder 内核 + +内部实现采用“同一套 chart builder 绑定不同 DSL map”的策略: + +1. report 的每个 `page.chart` 在 Yjs 中保存为独立 `Y.Map`。 +2. `ReportPageBuilder.chart` 直接复用 `VBIChartBuilder`,但绑定到 `page.chart` 子树,而不是根 `doc.getMap('dsl')`。 +3. `VBI.createChart(...)` 只是“chart builder 绑定根 DSL map”的特例;report page 内则绑定子 map。 +4. `VBIReportBuilderOptions` 只负责把 chart 相关 adapters/options 透传给每个 `page.chart` builder。 + +## Reference + +`packages/vbi/docs/2026-03-23-create-chart/adr.md`, `packages/vbi/src/vbi/create-vbi.ts`, `packages/vbi/src/vbi/generate-empty-dsl.ts`, `packages/vbi/src/builder/builder.ts` +`packages/vbi/src/types/builder/VBIInterface.ts`, `packages/vbi/src/types/dsl/vbi/vbi.ts`, `packages/vbi/docs/todo3-create-report/goal.md` + +## 淘汰内容概述 + +- 不把 page 设计成通用 `blocks: Array` +- 不继续保留泛化的 `types/dsl` 目录名 +- 不在 report 根节点重复保存 chart 的 `connectorId` 等字段 +- 不新增 report 级 `buildVQuery()` / `buildVSeed()` +- 不为 report 另写一套独立 chart 编辑与 lowering 逻辑 +- 不在首期支持“一个 page 多个 chart”或富文本样式系统 diff --git a/packages/vbi/docs/todo3-create-report/goal.md b/packages/vbi/docs/todo3-create-report/goal.md index 5b1bffd3e1..0ad42c48f6 100644 --- a/packages/vbi/docs/todo3-create-report/goal.md +++ b/packages/vbi/docs/todo3-create-report/goal.md @@ -5,6 +5,7 @@ 这里是 packages/vbi 计划要完成的开发任务: [] vbi内新增全新功能, VBI.createReport, 负责创建一个报告容器, 用于组织多个 chart -[] 一个report包含多个page -[] 一个page包含一个chart, 一个文本 +[] 一个report包含多个page, 一个page包含一个chart, 一个文本 +[] 设计一个合适的VBIReportDSL, 包括zod schema, 与当前的 VBIChartDSL 保持一致风格. +[] 一个VBIReportDSL应该包含多个page, 每个page包含一个VBIChartDSL, 一个文本. [] 需要设计好 reportBuilder, 使用风格、API风格、实现风格应该和chartBuilder是一致的.(vbi) diff --git a/packages/vbi/docs/todo3-create-report/plan.md b/packages/vbi/docs/todo3-create-report/plan.md new file mode 100644 index 0000000000..6c2d0fbaef --- /dev/null +++ b/packages/vbi/docs/todo3-create-report/plan.md @@ -0,0 +1,110 @@ +# 执行计划: VBI createReport 与 ReportBuilder + +> 基于 ADR: `./adr.md` +> TDD 驱动: 先锁定 schema 和 API,再做目录重组与实现 + +## 范围 + +本计划只覆盖 `packages/vbi`,包含 `createReport` 根入口、`types/dsl -> types/chartDSL` 重命名、`types/reportDSL` 新增、`VBIReportDSL` / `VBIReportBuilder` / `reportBuilder.page.*` 落地,以及相关测试与生成物更新;不包含 report 级 `buildVQuery()` / `buildVSeed()`、多 chart page、rich-text 系统。 + +## Phase 1: 先锁定对外行为 + +### 1.1 Schema 测试 + +**测试文件**: `packages/vbi/tests/types/reportSchemas.test.ts`(新增) + +测试内容: + +1. `zVBIReportDSL` 正确 parse 最小 report DSL。 +2. `page.title` / `page.chart` / `page.text` 缺失时被 reject。 +3. `page.chart` 继续复用 `zVBIChartDSL` 的校验结果。 +4. `generateEmptyReportDSL()` 与 `generateEmptyReportPageDSL(connectorId)` 的默认值稳定。 + +### 1.2 Builder API 测试 + +**测试文件**: `packages/vbi/tests/builder/reportBuilder.test.ts`(新增) + +测试内容: + +1. `VBI.createReport(report)` 与 `createVBI(...).createReport(report)` 可正常创建 builder。 +2. `reportBuilder.page.add('Story One', page => page.setChart(chartBuilder).setText('hello world'))` 输出正确 DSL。 +3. `reportBuilder.page.remove(id)` / `update(id, callback)` 行为正确。 +4. `setChart(chartBuilder)` 复制 `chartBuilder.build()` 结果,而不是共享 builder 实例。 +5. `build()` / `isEmpty()` / `applyUpdate()` / `encodeStateAsUpdate()` 行为稳定。 + +## Phase 2: ChartDSL 目录重命名与 ReportDSL 类型落地 + +**改动范围**: + +- `packages/vbi/src/types/dsl/**` -> `packages/vbi/src/types/chartDSL/**` +- `packages/vbi/src/types/reportDSL/**`(新增) +- `packages/vbi/src/types/index.ts` + +改动内容: + +1. 将现有 chart schema、类型、导出整体迁移到 `types/chartDSL`。 +2. 新增 `report.ts` / `page.ts` / `text.ts`,定义 `VBIReportDSL`、`VBIReportPageDSL`、`VBIReportTextDSL` 与 zod schema。 +3. 更新所有 `src/**`、`tests/**` 内部 import,消除对旧 `types/dsl` 路径的直接依赖。 + +## Phase 3: 空 DSL helper 与 Builder 内核准备 + +**改动文件**: + +- `packages/vbi/src/vbi/generate-empty-report-dsl.ts`(新增) +- `packages/vbi/src/vbi/generate-empty-report-page-dsl.ts`(新增) +- `packages/vbi/src/builder/builder.ts` +- `packages/vbi/src/builder/modules/**` +- `packages/vbi/src/types/builder/**` + +改动内容: + +1. 新增 `generateEmptyReportDSL()` 与 `generateEmptyReportPageDSL(connectorId)`。 +2. 将 `VBIChartBuilder` 的内部实现抽成“可绑定任意 DSL map”的模式,支持 page.chart 复用。 +3. 保持根 `createChart(...)` 行为不变,只把“绑定根 map”变成特例。 + +## Phase 4: ReportBuilder 与 page API + +**改动文件**: + +- `packages/vbi/src/builder/report-builder.ts`(新增) +- `packages/vbi/src/builder/features/report-page/page-collection-builder.ts`(新增) +- `packages/vbi/src/builder/features/report-page/page-builder.ts`(新增) +- `packages/vbi/src/builder/index.ts` +- `packages/vbi/src/types/builder/VBIInterface.ts` + +改动内容: + +1. 新增 `VBIReportBuilder` 与 `VBIReportBuilderInterface`。 +2. 提供 `reportBuilder.page.add/remove/update/get` 主入口。 +3. `page.add(title, callback)` 把第一个参数写入 `page.title`。 +4. `ReportPageBuilder` 提供 `setChart(chartBuilder)` 与 `setText(content)` 链式 API。 + +## Phase 5: 根入口、导出与包内迁移 + +**改动文件**: + +- `packages/vbi/src/vbi/create-vbi.ts` +- `packages/vbi/src/vbi/from/from-vbi-report-dsl-input.ts`(新增) +- `packages/vbi/src/index.ts` +- `packages/vbi/src/vbi.ts` + +改动内容: + +1. `createVBI()` 返回实例新增 `createReport(...)`。 +2. `VBI` 对外导出 `createReport`、`generateEmptyReportDSL`、`generateEmptyReportPageDSL`。 +3. 包内源码和测试默认切到 `types/chartDSL` / `types/reportDSL` 与 report 新 API。 + +## Phase 6: 生成物与验证 + +```bash +pnpm --filter=@visactor/vbi run g +pnpm --filter=@visactor/vbi run test +pnpm run lint +pnpm run typecheck +``` + +验收标准: + +1. `createReport`、`VBIReportDSL`、`reportBuilder.page.*` 通过测试并可稳定构建 DSL。 +2. `types/chartDSL` / `types/reportDSL` 路径清晰,包内无旧 `types/dsl` 残留引用。 +3. 生成物 diff 只反映 report 能力与目录命名收敛,不引入无关行为变化。 diff --git a/packages/vbi/scripts/build-api.mjs b/packages/vbi/scripts/build-api.mjs index feae3ff067..c8f59632a4 100644 --- a/packages/vbi/scripts/build-api.mjs +++ b/packages/vbi/scripts/build-api.mjs @@ -14,102 +14,166 @@ import { Project, SyntaxKind } from 'ts-morph' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -const BUILDER_DIR = path.resolve(__dirname, '../src/builder') +const BUILDER_ROOTS = { + chart: path.resolve(__dirname, '../src/chart-builder'), + report: path.resolve(__dirname, '../src/report-builder'), +} const OUTPUT_DIR = path.resolve(__dirname, '../../../apps/website/docs/zh-CN/vbi/api') // ============================================================================ // 配置 // ============================================================================ -const BUILDER_CONFIG = [ - { name: 'Builder', file: 'builder.ts', category: 'main' }, - { - name: 'chartType', - label: 'chartType', - file: 'features/chart-type/chart-type-builder.ts', - category: 'features', - }, - { name: 'measures', label: 'measures', file: 'features/measures/mea-builder.ts', category: 'features' }, - { - name: 'dimensions', - label: 'dimensions', - file: 'features/dimensions/dim-builder.ts', - category: 'features', - }, - { - name: 'whereFilter', - label: 'whereFilter', - file: 'features/whereFilter/where-builder.ts', - category: 'features', - }, - { - name: 'havingFilter', - label: 'havingFilter', - file: 'features/havingFilter/having-builder.ts', - category: 'features', - }, - { - name: 'theme', - label: 'theme', - file: 'features/theme/theme-builder.ts', - category: 'features', - }, - { - name: 'locale', - label: 'locale', - file: 'features/locale/locale-builder.ts', - category: 'features', - }, +const API_SECTIONS = [ { - name: 'limit', - label: 'limit', - file: 'features/limit/limit-builder.ts', - category: 'features', + name: 'chartBuilder', + label: 'chartBuilder', + root: 'chart', + index: { + file: 'builder.ts', + displayName: 'VBIChartBuilder', + }, + items: [ + { + type: 'file', + name: 'chartType', + label: 'chartBuilder.chartType', + file: 'features/chart-type/chart-type-builder.ts', + displayName: 'ChartTypeBuilder', + }, + { + type: 'dir', + name: 'measures', + label: 'chartBuilder.measures', + file: 'features/measures/mea-builder.ts', + displayName: 'MeasuresBuilder', + children: [ + { + name: 'measureNode', + label: 'measureNode', + file: 'features/measures/mea-node-builder.ts', + displayName: 'MeasureNodeBuilder', + }, + ], + }, + { + type: 'dir', + name: 'dimensions', + label: 'chartBuilder.dimensions', + file: 'features/dimensions/dim-builder.ts', + displayName: 'DimensionsBuilder', + children: [ + { + name: 'dimensionNode', + label: 'dimensionNode', + file: 'features/dimensions/dim-node-builder.ts', + displayName: 'DimensionNodeBuilder', + }, + ], + }, + { + type: 'dir', + name: 'whereFilter', + label: 'chartBuilder.whereFilter', + file: 'features/whereFilter/where-builder.ts', + displayName: 'WhereFilterBuilder', + children: [ + { + name: 'whereNode', + label: 'whereNode', + file: 'features/whereFilter/where-node-builder.ts', + displayName: 'WhereFilterNodeBuilder', + }, + { + name: 'whereGroup', + label: 'whereGroup', + file: 'features/whereFilter/where-group-builder.ts', + displayName: 'WhereGroupBuilder', + }, + ], + }, + { + type: 'dir', + name: 'havingFilter', + label: 'chartBuilder.havingFilter', + file: 'features/havingFilter/having-builder.ts', + displayName: 'HavingFilterBuilder', + children: [ + { + name: 'havingNode', + label: 'havingNode', + file: 'features/havingFilter/having-node-builder.ts', + displayName: 'HavingFilterNodeBuilder', + }, + { + name: 'havingGroup', + label: 'havingGroup', + file: 'features/havingFilter/having-group-builder.ts', + displayName: 'HavingGroupBuilder', + }, + ], + }, + { + type: 'file', + name: 'theme', + label: 'chartBuilder.theme', + file: 'features/theme/theme-builder.ts', + displayName: 'ThemeBuilder', + }, + { + type: 'file', + name: 'locale', + label: 'chartBuilder.locale', + file: 'features/locale/locale-builder.ts', + displayName: 'LocaleBuilder', + }, + { + type: 'file', + name: 'limit', + label: 'chartBuilder.limit', + file: 'features/limit/limit-builder.ts', + displayName: 'LimitBuilder', + }, + { + type: 'file', + name: 'undoManager', + label: 'chartBuilder.undoManager', + file: 'features/undo-manager/undo-manager.ts', + displayName: 'UndoManager', + }, + ], }, { - name: 'undoManager', - label: 'undoManager', - file: 'features/undo-manager/undo-manager.ts', - category: 'features', - }, -] - -const NODE_BUILDER_CONFIG = [ - { - parent: 'measures', - name: 'MeasureNodeBuilder', - label: 'measure-node', - file: 'features/measures/mea-node-builder.ts', - }, - { - parent: 'dimensions', - name: 'DimensionNodeBuilder', - label: 'dimension-node', - file: 'features/dimensions/dim-node-builder.ts', - }, - { - parent: 'whereFilter', - name: 'WhereNodeBuilder', - label: 'where-node', - file: 'features/whereFilter/where-node-builder.ts', - }, - { - parent: 'whereFilter', - name: 'WhereGroupBuilder', - label: 'where-group', - file: 'features/whereFilter/where-group-builder.ts', - }, - { - parent: 'havingFilter', - name: 'HavingFilterNodeBuilder', - label: 'having-node', - file: 'features/havingFilter/having-node-builder.ts', - }, - { - parent: 'havingFilter', - name: 'HavingGroupBuilder', - label: 'having-group', - file: 'features/havingFilter/having-group-builder.ts', + name: 'reportBuilder', + label: 'reportBuilder', + root: 'report', + index: { + file: 'builder.ts', + displayName: 'VBIReportBuilder', + }, + items: [ + { + type: 'dir', + name: 'page', + label: 'reportBuilder.page', + file: 'features/page/page-collection-builder.ts', + displayName: 'ReportPageCollectionBuilder', + children: [ + { + name: 'reportPage', + label: 'reportPage', + file: 'features/page/page-builder.ts', + displayName: 'ReportPageBuilder', + }, + { + name: 'reportText', + label: 'reportText', + file: 'features/page/text-builder.ts', + displayName: 'ReportTextBuilder', + }, + ], + }, + ], }, ] @@ -117,12 +181,15 @@ const NODE_BUILDER_CONFIG = [ // 工具函数 // ============================================================================ -const kebabCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase() - const ensureDir = (dirPath) => { if (!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true }) } +const resetDir = (dirPath) => { + fs.rmSync(dirPath, { recursive: true, force: true }) + fs.mkdirSync(dirPath, { recursive: true }) +} + const writeFile = (filePath, content) => fs.writeFileSync(filePath, content, 'utf-8') const writeJson = (filePath, data) => writeFile(filePath, JSON.stringify(data, null, 2)) @@ -130,6 +197,14 @@ const writeJson = (filePath, data) => writeFile(filePath, JSON.stringify(data, n /** 转义 markdown table 中的 | 字符 */ const escapeTableCell = (str) => str.replace(/\|/g, '\\|') +const resolveBuilderPath = (config) => { + const root = BUILDER_ROOTS[config.root] + if (!root) { + throw new Error(`Unknown builder root "${config.root}"`) + } + return path.join(root, config.file) +} + // ============================================================================ // 类型解析 // ============================================================================ @@ -250,6 +325,7 @@ const parseClass = (filePath) => { // 普通方法 for (const method of classDecl.getMethods()) { + if (method.hasModifier(SyntaxKind.PrivateKeyword)) continue methods.push({ name: method.getName(), params: parseParams(method), @@ -330,9 +406,9 @@ const renderMethodDoc = (method) => { } const renderBuilderDoc = (config) => { - const filePath = path.join(BUILDER_DIR, config.file) + const filePath = resolveBuilderPath(config) const { description, properties, methods } = parseClass(filePath) - const displayName = config.category === 'main' ? 'VBIChartBuilder' : config.label || config.name + const displayName = config.displayName || config.label || config.name const parts = [ `# ${displayName}`, @@ -346,13 +422,55 @@ const renderBuilderDoc = (config) => { return parts.filter(Boolean).join('\n\n') } -const renderNodeBuilderDoc = (config) => { - const filePath = path.join(BUILDER_DIR, config.file) - const { description, methods } = parseClass(filePath) +const writeDoc = (filePath, content) => writeFile(filePath, content) - const parts = [`# ${config.name}`, description || '', '## 方法', methods.map(renderMethodDoc).join('\n\n')] +const toDocConfig = (root, config) => ({ ...config, root }) - return parts.filter(Boolean).join('\n\n') +const createMetaFileEntry = (name, label) => ({ type: 'file', name, label }) + +const createMetaDirEntry = (name, label, collapsed = true) => ({ + type: 'dir', + name, + label, + collapsible: true, + collapsed, +}) + +const generateSection = (section) => { + const sectionDir = path.join(OUTPUT_DIR, section.name) + ensureDir(sectionDir) + + writeDoc(path.join(sectionDir, 'index.md'), renderBuilderDoc(toDocConfig(section.root, section.index))) + console.log(`Generated: ${section.name}/index.md`) + + const sectionMeta = [] + for (const item of section.items) { + if (item.type === 'file') { + writeDoc(path.join(sectionDir, `${item.name}.md`), renderBuilderDoc(toDocConfig(section.root, item))) + console.log(`Generated: ${section.name}/${item.name}.md`) + sectionMeta.push(createMetaFileEntry(item.name, item.label)) + continue + } + + const itemDir = path.join(sectionDir, item.name) + ensureDir(itemDir) + writeDoc(path.join(itemDir, 'index.md'), renderBuilderDoc(toDocConfig(section.root, item))) + console.log(`Generated: ${section.name}/${item.name}/index.md`) + + const childMeta = [] + for (const child of item.children || []) { + writeDoc(path.join(itemDir, `${child.name}.md`), renderBuilderDoc(toDocConfig(section.root, child))) + console.log(`Generated: ${section.name}/${item.name}/${child.name}.md`) + childMeta.push(createMetaFileEntry(child.name, child.label)) + } + + writeJson(path.join(itemDir, '_meta.json'), childMeta) + console.log(`Generated: ${section.name}/${item.name}/_meta.json`) + sectionMeta.push(createMetaDirEntry(item.name, item.label)) + } + + writeJson(path.join(sectionDir, '_meta.json'), sectionMeta) + console.log(`Generated: ${section.name}/_meta.json`) } // ============================================================================ @@ -362,63 +480,19 @@ const renderNodeBuilderDoc = (config) => { function generateDocs() { console.log('Building API docs from builder classes...\n') - ensureDir(OUTPUT_DIR) - - // 收集哪些 parent 拥有子文档 - const parentsWithChildren = new Set(NODE_BUILDER_CONFIG.map((n) => kebabCase(n.parent))) - - // 1. 生成 Builder 文档(主 builder + 子 builder 均输出到 OUTPUT_DIR) - const apiMeta = [] - for (const builder of BUILDER_CONFIG) { - const fileName = kebabCase(builder.name) - const md = renderBuilderDoc(builder) - - if (builder.category === 'main') { - writeFile(path.join(OUTPUT_DIR, 'builder.md'), md) - console.log(`Generated: builder.md`) - apiMeta.push({ type: 'file', name: 'builder', label: 'builder' }) - } else { - writeFile(path.join(OUTPUT_DIR, `${fileName}.md`), md) - console.log(`Generated: ${fileName}.md`) - - apiMeta.push({ - type: parentsWithChildren.has(fileName) ? 'dir' : 'file', - name: fileName, - label: `builder.${builder.label || builder.name}`, - collapsed: true, - }) - } - } - - // 2. 生成 NodeBuilder 文档 + _meta.json(按 parent 分组,输出到 OUTPUT_DIR/) - const nodeMetaByParent = {} - for (const nodeBuilder of NODE_BUILDER_CONFIG) { - const parentName = kebabCase(nodeBuilder.parent) - ensureDir(path.join(OUTPUT_DIR, parentName)) - - const md = renderNodeBuilderDoc(nodeBuilder) - writeFile(path.join(OUTPUT_DIR, parentName, `${nodeBuilder.label}.md`), md) - console.log(`Generated: ${parentName}/${nodeBuilder.label}.md`) - - if (!nodeMetaByParent[parentName]) nodeMetaByParent[parentName] = [] - nodeMetaByParent[parentName].push({ - type: 'file', - name: nodeBuilder.label, - label: nodeBuilder.label, - collapsed: true, - }) - } + resetDir(OUTPUT_DIR) - // 3. 生成 _meta.json 文件 - for (const [parentName, items] of Object.entries(nodeMetaByParent)) { - writeJson(path.join(OUTPUT_DIR, parentName, '_meta.json'), items) - console.log(`Generated: api/${parentName}/_meta.json`) + for (const section of API_SECTIONS) { + generateSection(section) } - writeJson(path.join(OUTPUT_DIR, '_meta.json'), apiMeta) + writeJson(path.join(OUTPUT_DIR, '_meta.json'), [ + createMetaFileEntry('index', 'API'), + ...API_SECTIONS.map((section) => createMetaDirEntry(section.name, section.label, false)), + ]) console.log('Generated: api/_meta.json') - writeFile(path.join(OUTPUT_DIR, 'index.md'), '---\noverview: true\n---\n') + writeFile(path.join(OUTPUT_DIR, 'index.md'), '---\noverview: true\ntitle: API\n---\n') console.log('Generated: index.md') // 4. 确保父级 _meta.json 包含 api 条目 @@ -430,9 +504,7 @@ function generateDocs() { console.log('Updated: _meta.json') } - console.log( - `\n✅ Generated API docs for ${BUILDER_CONFIG.length} builders and ${NODE_BUILDER_CONFIG.length} node-builders`, - ) + console.log(`\n✅ Generated API docs for ${API_SECTIONS.length} builder sections`) } generateDocs() diff --git a/packages/vbi/src/builder/adapters/index.ts b/packages/vbi/src/chart-builder/adapters/index.ts similarity index 100% rename from packages/vbi/src/builder/adapters/index.ts rename to packages/vbi/src/chart-builder/adapters/index.ts diff --git a/packages/vbi/src/builder/adapters/vquery-vseed/build-vquery.ts b/packages/vbi/src/chart-builder/adapters/vquery-vseed/build-vquery.ts similarity index 75% rename from packages/vbi/src/builder/adapters/vquery-vseed/build-vquery.ts rename to packages/vbi/src/chart-builder/adapters/vquery-vseed/build-vquery.ts index c16177e0e4..656b21b894 100644 --- a/packages/vbi/src/builder/adapters/vquery-vseed/build-vquery.ts +++ b/packages/vbi/src/chart-builder/adapters/vquery-vseed/build-vquery.ts @@ -1,4 +1,4 @@ -import { buildVQuery as buildVQueryPipeline } from 'src/pipeline' +import { buildVQuery as buildVQueryPipeline } from 'src/chart-builder/pipeline' import type { VBIChartQueryBuilder } from 'src/types' import type { DefaultVBIQueryDSL } from './types' diff --git a/packages/vbi/src/builder/adapters/vquery-vseed/build-vseed.ts b/packages/vbi/src/chart-builder/adapters/vquery-vseed/build-vseed.ts similarity index 93% rename from packages/vbi/src/builder/adapters/vquery-vseed/build-vseed.ts rename to packages/vbi/src/chart-builder/adapters/vquery-vseed/build-vseed.ts index c31ebca524..d767185a27 100644 --- a/packages/vbi/src/builder/adapters/vquery-vseed/build-vseed.ts +++ b/packages/vbi/src/chart-builder/adapters/vquery-vseed/build-vseed.ts @@ -1,7 +1,7 @@ import type { Dimension, Measure } from '@visactor/vseed' -import { DimensionsBuilder, MeasuresBuilder } from 'src/builder/features' +import { DimensionsBuilder, MeasuresBuilder } from 'src/chart-builder/features' import type { VBIChartSeedBuilder } from 'src/types' -import { getConnector } from 'src/builder/connector' +import { getConnector } from 'src/chart-builder/connector' import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from './types' export const buildVSeedDSL: VBIChartSeedBuilder = async ({ diff --git a/packages/vbi/src/builder/adapters/vquery-vseed/index.ts b/packages/vbi/src/chart-builder/adapters/vquery-vseed/index.ts similarity index 100% rename from packages/vbi/src/builder/adapters/vquery-vseed/index.ts rename to packages/vbi/src/chart-builder/adapters/vquery-vseed/index.ts diff --git a/packages/vbi/src/builder/adapters/vquery-vseed/types.ts b/packages/vbi/src/chart-builder/adapters/vquery-vseed/types.ts similarity index 100% rename from packages/vbi/src/builder/adapters/vquery-vseed/types.ts rename to packages/vbi/src/chart-builder/adapters/vquery-vseed/types.ts diff --git a/packages/vbi/src/builder/builder.ts b/packages/vbi/src/chart-builder/builder.ts similarity index 92% rename from packages/vbi/src/builder/builder.ts rename to packages/vbi/src/chart-builder/builder.ts index 484cf16379..6b88a6020c 100644 --- a/packages/vbi/src/builder/builder.ts +++ b/packages/vbi/src/chart-builder/builder.ts @@ -1,7 +1,7 @@ import * as Y from 'yjs' -import { resolveVBIChartBuilderAdapters } from 'src/builder/adapters/vquery-vseed' -import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/builder/adapters/vquery-vseed' +import { resolveVBIChartBuilderAdapters } from 'src/chart-builder/adapters/vquery-vseed' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed' import { DimensionsBuilder, MeasuresBuilder, @@ -46,9 +46,9 @@ export class VBIChartBuilder) { + constructor(doc: Y.Doc, options?: VBIChartBuilderOptions, dsl?: Y.Map) { this.doc = doc - this.dsl = doc.getMap('dsl') as Y.Map + this.dsl = (dsl ?? doc.getMap('dsl')) as Y.Map this.adapters = resolveVBIChartBuilderAdapters(options?.adapters) this.undoManager = new UndoManager(this.dsl) diff --git a/packages/vbi/src/builder/connector.ts b/packages/vbi/src/chart-builder/connector.ts similarity index 100% rename from packages/vbi/src/builder/connector.ts rename to packages/vbi/src/chart-builder/connector.ts diff --git a/packages/vbi/src/builder/features/chart-type/chart-type-builder.ts b/packages/vbi/src/chart-builder/features/chart-type/chart-type-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/chart-type/chart-type-builder.ts rename to packages/vbi/src/chart-builder/features/chart-type/chart-type-builder.ts diff --git a/packages/vbi/src/builder/features/chart-type/dimension-encoding.ts b/packages/vbi/src/chart-builder/features/chart-type/dimension-encoding.ts similarity index 100% rename from packages/vbi/src/builder/features/chart-type/dimension-encoding.ts rename to packages/vbi/src/chart-builder/features/chart-type/dimension-encoding.ts diff --git a/packages/vbi/src/builder/features/chart-type/index.ts b/packages/vbi/src/chart-builder/features/chart-type/index.ts similarity index 100% rename from packages/vbi/src/builder/features/chart-type/index.ts rename to packages/vbi/src/chart-builder/features/chart-type/index.ts diff --git a/packages/vbi/src/builder/features/chart-type/measure-encoding.ts b/packages/vbi/src/chart-builder/features/chart-type/measure-encoding.ts similarity index 100% rename from packages/vbi/src/builder/features/chart-type/measure-encoding.ts rename to packages/vbi/src/chart-builder/features/chart-type/measure-encoding.ts diff --git a/packages/vbi/src/builder/features/chart-type/reapply-dimension-encodings.ts b/packages/vbi/src/chart-builder/features/chart-type/reapply-dimension-encodings.ts similarity index 100% rename from packages/vbi/src/builder/features/chart-type/reapply-dimension-encodings.ts rename to packages/vbi/src/chart-builder/features/chart-type/reapply-dimension-encodings.ts diff --git a/packages/vbi/src/builder/features/chart-type/reapply-measure-encodings.ts b/packages/vbi/src/chart-builder/features/chart-type/reapply-measure-encodings.ts similarity index 100% rename from packages/vbi/src/builder/features/chart-type/reapply-measure-encodings.ts rename to packages/vbi/src/chart-builder/features/chart-type/reapply-measure-encodings.ts diff --git a/packages/vbi/src/builder/features/dimensions/dim-builder.ts b/packages/vbi/src/chart-builder/features/dimensions/dim-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/dimensions/dim-builder.ts rename to packages/vbi/src/chart-builder/features/dimensions/dim-builder.ts diff --git a/packages/vbi/src/builder/features/dimensions/dim-node-builder.ts b/packages/vbi/src/chart-builder/features/dimensions/dim-node-builder.ts similarity index 96% rename from packages/vbi/src/builder/features/dimensions/dim-node-builder.ts rename to packages/vbi/src/chart-builder/features/dimensions/dim-node-builder.ts index dc5a1dd4a2..49d620bb47 100644 --- a/packages/vbi/src/builder/features/dimensions/dim-node-builder.ts +++ b/packages/vbi/src/chart-builder/features/dimensions/dim-node-builder.ts @@ -1,5 +1,5 @@ import * as Y from 'yjs' -import type { VBIDimension, VBISort } from '../../../types' +import type { VBIDimension, VBISort } from 'src/types' /** * @description 维度节点构建器,用于配置单个维度 diff --git a/packages/vbi/src/builder/features/dimensions/dimension-utils.ts b/packages/vbi/src/chart-builder/features/dimensions/dimension-utils.ts similarity index 100% rename from packages/vbi/src/builder/features/dimensions/dimension-utils.ts rename to packages/vbi/src/chart-builder/features/dimensions/dimension-utils.ts diff --git a/packages/vbi/src/builder/features/dimensions/index.ts b/packages/vbi/src/chart-builder/features/dimensions/index.ts similarity index 100% rename from packages/vbi/src/builder/features/dimensions/index.ts rename to packages/vbi/src/chart-builder/features/dimensions/index.ts diff --git a/packages/vbi/src/builder/features/havingFilter/having-builder.ts b/packages/vbi/src/chart-builder/features/havingFilter/having-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/havingFilter/having-builder.ts rename to packages/vbi/src/chart-builder/features/havingFilter/having-builder.ts diff --git a/packages/vbi/src/builder/features/havingFilter/having-group-builder.ts b/packages/vbi/src/chart-builder/features/havingFilter/having-group-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/havingFilter/having-group-builder.ts rename to packages/vbi/src/chart-builder/features/havingFilter/having-group-builder.ts diff --git a/packages/vbi/src/builder/features/havingFilter/having-node-builder.ts b/packages/vbi/src/chart-builder/features/havingFilter/having-node-builder.ts similarity index 95% rename from packages/vbi/src/builder/features/havingFilter/having-node-builder.ts rename to packages/vbi/src/chart-builder/features/havingFilter/having-node-builder.ts index 728f44acde..02c7fc6032 100644 --- a/packages/vbi/src/builder/features/havingFilter/having-node-builder.ts +++ b/packages/vbi/src/chart-builder/features/havingFilter/having-node-builder.ts @@ -1,5 +1,5 @@ import * as Y from 'yjs' -import { VBIHavingFilter, VBIHavingAggregate } from '../../../types' +import type { VBIHavingFilter, VBIHavingAggregate } from 'src/types' /** * @description Having 过滤节点构建器,用于配置单个 Having 过滤条件 diff --git a/packages/vbi/src/builder/features/havingFilter/having-utils.ts b/packages/vbi/src/chart-builder/features/havingFilter/having-utils.ts similarity index 100% rename from packages/vbi/src/builder/features/havingFilter/having-utils.ts rename to packages/vbi/src/chart-builder/features/havingFilter/having-utils.ts diff --git a/packages/vbi/src/builder/features/havingFilter/index.ts b/packages/vbi/src/chart-builder/features/havingFilter/index.ts similarity index 100% rename from packages/vbi/src/builder/features/havingFilter/index.ts rename to packages/vbi/src/chart-builder/features/havingFilter/index.ts diff --git a/packages/vbi/src/builder/features/index.ts b/packages/vbi/src/chart-builder/features/index.ts similarity index 100% rename from packages/vbi/src/builder/features/index.ts rename to packages/vbi/src/chart-builder/features/index.ts diff --git a/packages/vbi/src/builder/features/limit/index.ts b/packages/vbi/src/chart-builder/features/limit/index.ts similarity index 100% rename from packages/vbi/src/builder/features/limit/index.ts rename to packages/vbi/src/chart-builder/features/limit/index.ts diff --git a/packages/vbi/src/builder/features/limit/limit-builder.ts b/packages/vbi/src/chart-builder/features/limit/limit-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/limit/limit-builder.ts rename to packages/vbi/src/chart-builder/features/limit/limit-builder.ts diff --git a/packages/vbi/src/builder/features/locale/index.ts b/packages/vbi/src/chart-builder/features/locale/index.ts similarity index 100% rename from packages/vbi/src/builder/features/locale/index.ts rename to packages/vbi/src/chart-builder/features/locale/index.ts diff --git a/packages/vbi/src/builder/features/locale/locale-builder.ts b/packages/vbi/src/chart-builder/features/locale/locale-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/locale/locale-builder.ts rename to packages/vbi/src/chart-builder/features/locale/locale-builder.ts diff --git a/packages/vbi/src/builder/features/measures/index.ts b/packages/vbi/src/chart-builder/features/measures/index.ts similarity index 100% rename from packages/vbi/src/builder/features/measures/index.ts rename to packages/vbi/src/chart-builder/features/measures/index.ts diff --git a/packages/vbi/src/builder/features/measures/mea-builder.ts b/packages/vbi/src/chart-builder/features/measures/mea-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/measures/mea-builder.ts rename to packages/vbi/src/chart-builder/features/measures/mea-builder.ts diff --git a/packages/vbi/src/builder/features/measures/mea-node-builder.ts b/packages/vbi/src/chart-builder/features/measures/mea-node-builder.ts similarity index 96% rename from packages/vbi/src/builder/features/measures/mea-node-builder.ts rename to packages/vbi/src/chart-builder/features/measures/mea-node-builder.ts index 9a3b3f58a2..5e4a248eca 100644 --- a/packages/vbi/src/builder/features/measures/mea-node-builder.ts +++ b/packages/vbi/src/chart-builder/features/measures/mea-node-builder.ts @@ -1,5 +1,5 @@ import * as Y from 'yjs' -import type { VBIMeasure, VBIMeasureFormat, VBISort } from '../../../types' +import type { VBIMeasure, VBIMeasureFormat, VBISort } from 'src/types' /** * @description 度量节点构建器,用于配置单个度量 diff --git a/packages/vbi/src/builder/features/measures/measure-utils.ts b/packages/vbi/src/chart-builder/features/measures/measure-utils.ts similarity index 100% rename from packages/vbi/src/builder/features/measures/measure-utils.ts rename to packages/vbi/src/chart-builder/features/measures/measure-utils.ts diff --git a/packages/vbi/src/builder/features/theme/index.ts b/packages/vbi/src/chart-builder/features/theme/index.ts similarity index 100% rename from packages/vbi/src/builder/features/theme/index.ts rename to packages/vbi/src/chart-builder/features/theme/index.ts diff --git a/packages/vbi/src/builder/features/theme/theme-builder.ts b/packages/vbi/src/chart-builder/features/theme/theme-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/theme/theme-builder.ts rename to packages/vbi/src/chart-builder/features/theme/theme-builder.ts diff --git a/packages/vbi/src/builder/features/undo-manager/index.ts b/packages/vbi/src/chart-builder/features/undo-manager/index.ts similarity index 100% rename from packages/vbi/src/builder/features/undo-manager/index.ts rename to packages/vbi/src/chart-builder/features/undo-manager/index.ts diff --git a/packages/vbi/src/builder/features/undo-manager/undo-manager.ts b/packages/vbi/src/chart-builder/features/undo-manager/undo-manager.ts similarity index 100% rename from packages/vbi/src/builder/features/undo-manager/undo-manager.ts rename to packages/vbi/src/chart-builder/features/undo-manager/undo-manager.ts diff --git a/packages/vbi/src/builder/features/whereFilter/index.ts b/packages/vbi/src/chart-builder/features/whereFilter/index.ts similarity index 100% rename from packages/vbi/src/builder/features/whereFilter/index.ts rename to packages/vbi/src/chart-builder/features/whereFilter/index.ts diff --git a/packages/vbi/src/builder/features/whereFilter/where-builder.ts b/packages/vbi/src/chart-builder/features/whereFilter/where-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/whereFilter/where-builder.ts rename to packages/vbi/src/chart-builder/features/whereFilter/where-builder.ts diff --git a/packages/vbi/src/builder/features/whereFilter/where-group-builder.ts b/packages/vbi/src/chart-builder/features/whereFilter/where-group-builder.ts similarity index 100% rename from packages/vbi/src/builder/features/whereFilter/where-group-builder.ts rename to packages/vbi/src/chart-builder/features/whereFilter/where-group-builder.ts diff --git a/packages/vbi/src/builder/features/whereFilter/where-node-builder.ts b/packages/vbi/src/chart-builder/features/whereFilter/where-node-builder.ts similarity index 95% rename from packages/vbi/src/builder/features/whereFilter/where-node-builder.ts rename to packages/vbi/src/chart-builder/features/whereFilter/where-node-builder.ts index 0c32a3e2f0..0b1056d241 100644 --- a/packages/vbi/src/builder/features/whereFilter/where-node-builder.ts +++ b/packages/vbi/src/chart-builder/features/whereFilter/where-node-builder.ts @@ -1,5 +1,5 @@ import * as Y from 'yjs' -import type { VBIWhereDatePredicate, VBIWhereFilter } from '../../../types' +import type { VBIWhereDatePredicate, VBIWhereFilter } from 'src/types' /** * @description Where 过滤节点构建器,用于配置单个 Where 过滤条件 diff --git a/packages/vbi/src/builder/features/whereFilter/where-utils.ts b/packages/vbi/src/chart-builder/features/whereFilter/where-utils.ts similarity index 100% rename from packages/vbi/src/builder/features/whereFilter/where-utils.ts rename to packages/vbi/src/chart-builder/features/whereFilter/where-utils.ts diff --git a/packages/vbi/src/builder/index.ts b/packages/vbi/src/chart-builder/index.ts similarity index 81% rename from packages/vbi/src/builder/index.ts rename to packages/vbi/src/chart-builder/index.ts index b881dbd3af..f0901b2ba4 100644 --- a/packages/vbi/src/builder/index.ts +++ b/packages/vbi/src/chart-builder/index.ts @@ -1,12 +1,11 @@ export { VBIChartBuilder } from './builder' -export { VBI } from '../vbi' -export * from './adapters' export { MeasuresBuilder, DimensionsBuilder, ChartTypeBuilder, - HavingFilterBuilder, WhereFilterBuilder, + WhereGroupBuilder, + HavingFilterBuilder, ThemeBuilder, LocaleBuilder, LimitBuilder, diff --git a/packages/vbi/src/builder/modules/apply-update.ts b/packages/vbi/src/chart-builder/modules/apply-update.ts similarity index 100% rename from packages/vbi/src/builder/modules/apply-update.ts rename to packages/vbi/src/chart-builder/modules/apply-update.ts diff --git a/packages/vbi/src/builder/modules/build.ts b/packages/vbi/src/chart-builder/modules/build.ts similarity index 100% rename from packages/vbi/src/builder/modules/build.ts rename to packages/vbi/src/chart-builder/modules/build.ts diff --git a/packages/vbi/src/builder/modules/encode-state-as-update.ts b/packages/vbi/src/chart-builder/modules/encode-state-as-update.ts similarity index 100% rename from packages/vbi/src/builder/modules/encode-state-as-update.ts rename to packages/vbi/src/chart-builder/modules/encode-state-as-update.ts diff --git a/packages/vbi/src/builder/modules/get-schema.ts b/packages/vbi/src/chart-builder/modules/get-schema.ts similarity index 100% rename from packages/vbi/src/builder/modules/get-schema.ts rename to packages/vbi/src/chart-builder/modules/get-schema.ts diff --git a/packages/vbi/src/builder/modules/index.ts b/packages/vbi/src/chart-builder/modules/index.ts similarity index 100% rename from packages/vbi/src/builder/modules/index.ts rename to packages/vbi/src/chart-builder/modules/index.ts diff --git a/packages/vbi/src/builder/modules/is-empty.ts b/packages/vbi/src/chart-builder/modules/is-empty.ts similarity index 100% rename from packages/vbi/src/builder/modules/is-empty.ts rename to packages/vbi/src/chart-builder/modules/is-empty.ts diff --git a/packages/vbi/src/pipeline/index.ts b/packages/vbi/src/chart-builder/pipeline/index.ts similarity index 100% rename from packages/vbi/src/pipeline/index.ts rename to packages/vbi/src/chart-builder/pipeline/index.ts diff --git a/packages/vbi/src/pipeline/vqueryDSL/aggregateMap.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/aggregateMap.ts similarity index 100% rename from packages/vbi/src/pipeline/vqueryDSL/aggregateMap.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/aggregateMap.ts diff --git a/packages/vbi/src/pipeline/vqueryDSL/buildGroupBy.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildGroupBy.ts similarity index 90% rename from packages/vbi/src/pipeline/vqueryDSL/buildGroupBy.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildGroupBy.ts index 43599f9398..fcf9c3e60d 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/buildGroupBy.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildGroupBy.ts @@ -1,6 +1,6 @@ import type { VQueryDSL } from '@visactor/vquery' import type { buildPipe } from './types' -import { DimensionsBuilder } from '../../builder' +import { DimensionsBuilder } from '../../features' export const buildGroupBy: buildPipe = (queryDSL, context) => { const result = { ...queryDSL } diff --git a/packages/vbi/src/pipeline/vqueryDSL/buildHaving.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildHaving.ts similarity index 98% rename from packages/vbi/src/pipeline/vqueryDSL/buildHaving.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildHaving.ts index e9132d8ce9..e701a028d9 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/buildHaving.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildHaving.ts @@ -1,6 +1,6 @@ import type { VQueryDSL } from '@visactor/vquery' import type { buildPipe } from './types' -import type { VBIHavingClause, VBIHavingFilter, VBIHavingGroup } from '../../types' +import type { VBIHavingClause, VBIHavingFilter, VBIHavingGroup } from 'src/types' import { mapAggregateForVQuery } from './aggregateMap' export const buildHaving: buildPipe = (queryDSL, context) => { diff --git a/packages/vbi/src/pipeline/vqueryDSL/buildLimit.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildLimit.ts similarity index 100% rename from packages/vbi/src/pipeline/vqueryDSL/buildLimit.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildLimit.ts diff --git a/packages/vbi/src/pipeline/vqueryDSL/buildOrderBy.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildOrderBy.ts similarity index 93% rename from packages/vbi/src/pipeline/vqueryDSL/buildOrderBy.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildOrderBy.ts index f74f1a96a4..4f780c20c0 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/buildOrderBy.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildOrderBy.ts @@ -1,5 +1,5 @@ import type { VQueryDSL } from '@visactor/vquery' -import { DimensionsBuilder, MeasuresBuilder } from '../../builder' +import { DimensionsBuilder, MeasuresBuilder } from '../../features' import type { buildPipe } from './types' const toOrderItem = (node: { id: string; sort?: { order: 'asc' | 'desc' } }) => ({ diff --git a/packages/vbi/src/pipeline/vqueryDSL/buildSelect.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildSelect.ts similarity index 94% rename from packages/vbi/src/pipeline/vqueryDSL/buildSelect.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildSelect.ts index 2d36d7bb5b..c00aa1e633 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/buildSelect.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildSelect.ts @@ -1,6 +1,6 @@ import type { VQueryDSL } from '@visactor/vquery' import type { buildPipe } from './types' -import { MeasuresBuilder, DimensionsBuilder } from '../../builder' +import { MeasuresBuilder, DimensionsBuilder } from '../../features' import { mapAggregateForVQuery, mapDimensionAggregateForVQuery } from './aggregateMap' export const buildSelect: buildPipe = (queryDSL, context) => { diff --git a/packages/vbi/src/pipeline/vqueryDSL/buildWhere.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildWhere.ts similarity index 98% rename from packages/vbi/src/pipeline/vqueryDSL/buildWhere.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildWhere.ts index ab4a3d4975..14852f9f17 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/buildWhere.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/buildWhere.ts @@ -1,6 +1,6 @@ import type { VQueryDSL } from '@visactor/vquery' import type { buildPipe } from './types' -import type { VBIWhereDatePredicate, VBIWhereFilter, VBIWhereClause, VBIWhereGroup } from '../../types' +import type { VBIWhereDatePredicate, VBIWhereFilter, VBIWhereClause, VBIWhereGroup } from 'src/types' import { resolveDatePredicate } from './resolveDatePredicate' export const buildWhere: buildPipe = (queryDSL, context) => { diff --git a/packages/vbi/src/pipeline/vqueryDSL/index.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/index.ts similarity index 91% rename from packages/vbi/src/pipeline/vqueryDSL/index.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/index.ts index ea94e46105..07a12d88ff 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/index.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/index.ts @@ -1,6 +1,6 @@ import { pipe } from 'remeda' import type { VQueryDSL } from '@visactor/vquery' -import type { VBIChartBuilderInterface, VBIChartDSL } from '../../types' +import type { VBIChartBuilderInterface, VBIChartDSL } from 'src/types' import type { buildPipe } from './types' import { buildSelect } from './buildSelect' import { buildGroupBy } from './buildGroupBy' diff --git a/packages/vbi/src/pipeline/vqueryDSL/resolveDatePredicate.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/resolveDatePredicate.ts similarity index 99% rename from packages/vbi/src/pipeline/vqueryDSL/resolveDatePredicate.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/resolveDatePredicate.ts index d0e79c4efd..0a038b775e 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/resolveDatePredicate.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/resolveDatePredicate.ts @@ -1,4 +1,4 @@ -import type { VBIWhereDateBounds, VBIWhereDatePredicate } from '../../types' +import type { VBIWhereDateBounds, VBIWhereDatePredicate } from 'src/types' export type DateRange = { start: string diff --git a/packages/vbi/src/pipeline/vqueryDSL/types.ts b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/types.ts similarity index 76% rename from packages/vbi/src/pipeline/vqueryDSL/types.ts rename to packages/vbi/src/chart-builder/pipeline/vqueryDSL/types.ts index f9c0ac86db..4af9465693 100644 --- a/packages/vbi/src/pipeline/vqueryDSL/types.ts +++ b/packages/vbi/src/chart-builder/pipeline/vqueryDSL/types.ts @@ -1,5 +1,5 @@ import type { VQueryDSL } from '@visactor/vquery' -import type { VBIChartBuilderInterface, VBIChartDSL } from '../../types' +import type { VBIChartBuilderInterface, VBIChartDSL } from 'src/types' export type buildPipeContext = { vbiDSL: VBIChartDSL diff --git a/packages/vbi/src/index.ts b/packages/vbi/src/index.ts index 78585899ee..b8303aa271 100644 --- a/packages/vbi/src/index.ts +++ b/packages/vbi/src/index.ts @@ -1,9 +1,8 @@ export { VBI } from './vbi' export { createVBI } from './vbi/create-vbi' export type { VBIInstance } from './vbi/create-vbi' -export { VBIChartBuilder } from './builder' -export { defaultVBIChartBuilderAdapters, resolveVBIChartBuilderAdapters } from './builder/adapters' export { + VBIChartBuilder, MeasuresBuilder, DimensionsBuilder, ChartTypeBuilder, @@ -13,7 +12,9 @@ export { LocaleBuilder, LimitBuilder, UndoManager, -} from './builder' +} from './chart-builder' +export { VBIReportBuilder, ReportPageBuilder, ReportPageCollectionBuilder, ReportTextBuilder } from './report-builder' +export { defaultVBIChartBuilderAdapters, resolveVBIChartBuilderAdapters } from './chart-builder/adapters' export * from './types' export { id, @@ -24,4 +25,4 @@ export { preorderTraverse, findTreeNodesBy, } from './utils' -export { buildVQuery } from './pipeline' +export { buildVQuery } from './chart-builder/pipeline' diff --git a/packages/vbi/src/report-builder/builder.ts b/packages/vbi/src/report-builder/builder.ts new file mode 100644 index 0000000000..fb6b409059 --- /dev/null +++ b/packages/vbi/src/report-builder/builder.ts @@ -0,0 +1,42 @@ +import * as Y from 'yjs' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' +import type { VBIReportDSL, VBIReportBuilderInterface, VBIReportBuilderOptions } from 'src/types' +import { UndoManager, ReportPageCollectionBuilder } from './features' +import { applyUpdateToDoc, encodeDocStateAsUpdate, buildVBIReportDSL, isEmptyVBIReportDSL } from './modules' +import { getOrCreateReportPages } from 'src/vbi/from/report-page-y-map' + +export class VBIReportBuilder + implements VBIReportBuilderInterface +{ + public doc: Y.Doc + public dsl: Y.Map + public undoManager: UndoManager + public page: ReportPageCollectionBuilder + + constructor(doc: Y.Doc, options?: VBIReportBuilderOptions) { + this.doc = doc + this.dsl = doc.getMap('dsl') as Y.Map + + doc.transact(() => { + getOrCreateReportPages(this.dsl) + if (this.dsl.get('version') === undefined) { + this.dsl.set('version', 0) + } + }) + + this.undoManager = new UndoManager(this.dsl) + this.page = new ReportPageCollectionBuilder(this, doc, this.dsl, options) + } + + public applyUpdate = (update: Uint8Array, transactionOrigin?: any) => { + return applyUpdateToDoc(this.doc, update, transactionOrigin) + } + + public encodeStateAsUpdate = (targetStateVector?: Uint8Array) => { + return encodeDocStateAsUpdate(this.doc, targetStateVector) + } + + public build = (): VBIReportDSL => buildVBIReportDSL(this.dsl) + + public isEmpty = (): boolean => isEmptyVBIReportDSL(this.dsl) +} diff --git a/packages/vbi/src/report-builder/features/index.ts b/packages/vbi/src/report-builder/features/index.ts new file mode 100644 index 0000000000..f9849b3def --- /dev/null +++ b/packages/vbi/src/report-builder/features/index.ts @@ -0,0 +1,2 @@ +export { ReportPageBuilder, ReportPageCollectionBuilder, ReportTextBuilder } from './page' +export { UndoManager } from 'src/chart-builder/features/undo-manager' diff --git a/packages/vbi/src/report-builder/features/page/index.ts b/packages/vbi/src/report-builder/features/page/index.ts new file mode 100644 index 0000000000..c12698f28b --- /dev/null +++ b/packages/vbi/src/report-builder/features/page/index.ts @@ -0,0 +1,3 @@ +export { ReportPageBuilder } from './page-builder' +export { ReportPageCollectionBuilder } from './page-collection-builder' +export { ReportTextBuilder } from './text-builder' diff --git a/packages/vbi/src/report-builder/features/page/page-builder.ts b/packages/vbi/src/report-builder/features/page/page-builder.ts new file mode 100644 index 0000000000..dfc64617b2 --- /dev/null +++ b/packages/vbi/src/report-builder/features/page/page-builder.ts @@ -0,0 +1,51 @@ +import * as Y from 'yjs' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' +import type { VBIChartBuilderOptions, VBIChartDSLInput, VBIReportPageDSL } from 'src/types' +import { VBIChartBuilder } from 'src/chart-builder/builder' +import { fillVBIChartDSLMap } from 'src/vbi/from/fill-vbi-chart-dsl-map' +import { getOrCreateReportChartMap, getOrCreateReportTextMap } from 'src/vbi/from/report-page-y-map' +import { ReportTextBuilder } from './text-builder' + +const isChartBuilderLike = (value: unknown): value is { build: () => VBIChartDSLInput } => { + return typeof value === 'object' && value !== null && typeof (value as { build?: unknown }).build === 'function' +} + +export class ReportPageBuilder { + public chart: VBIChartBuilder + public text: ReportTextBuilder + + constructor( + private doc: Y.Doc, + private page: Y.Map, + chartOptions?: VBIChartBuilderOptions, + ) { + this.chart = new VBIChartBuilder(doc, chartOptions, getOrCreateReportChartMap(page)) + this.text = new ReportTextBuilder(getOrCreateReportTextMap(page)) + } + + getId(): string { + return this.page.get('id') + } + + setTitle(title: string): this { + this.page.set('title', title) + return this + } + + setChart(chartBuilder: VBIChartBuilder | VBIChartDSLInput): this { + const chartDSL = isChartBuilderLike(chartBuilder) ? chartBuilder.build() : chartBuilder + this.doc.transact(() => { + fillVBIChartDSLMap(this.chart.dsl, chartDSL) + }) + return this + } + + setText(content: string): this { + this.text.setContent(content) + return this + } + + toJSON(): VBIReportPageDSL { + return this.page.toJSON() as VBIReportPageDSL + } +} diff --git a/packages/vbi/src/report-builder/features/page/page-collection-builder.ts b/packages/vbi/src/report-builder/features/page/page-collection-builder.ts new file mode 100644 index 0000000000..fe9a6d61ed --- /dev/null +++ b/packages/vbi/src/report-builder/features/page/page-collection-builder.ts @@ -0,0 +1,75 @@ +import * as Y from 'yjs' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' +import type { VBIReportBuilderOptions } from 'src/types' +import type { VBIReportBuilder } from 'src/report-builder/builder' +import { generateEmptyReportPageDSL } from 'src/vbi/generate-empty-report-page-dsl' +import { createReportPageYMap, getOrCreateReportPages, locateReportPageIndexById } from 'src/vbi/from/report-page-y-map' +import { ReportPageBuilder } from './page-builder' + +export class ReportPageCollectionBuilder { + constructor( + private parent: VBIReportBuilder, + private doc: Y.Doc, + private dsl: Y.Map, + private options?: VBIReportBuilderOptions, + ) { + doc.transact(() => { + getOrCreateReportPages(this.dsl) + }) + } + + add( + title: string, + callback?: (page: ReportPageBuilder) => void, + ): VBIReportBuilder { + const pageMap = createReportPageYMap({ + ...generateEmptyReportPageDSL(), + title, + }) + + this.doc.transact(() => { + getOrCreateReportPages(this.dsl).push([pageMap]) + }) + + if (callback) { + callback(this.createPageBuilder(pageMap)) + } + + return this.parent + } + + remove(pageId: string): VBIReportBuilder { + this.doc.transact(() => { + const pages = getOrCreateReportPages(this.dsl) + const index = locateReportPageIndexById(pages, pageId) + if (index !== -1) { + pages.delete(index, 1) + } + }) + return this.parent + } + + update( + pageId: string, + callback: (page: ReportPageBuilder) => void, + ): VBIReportBuilder { + this.doc.transact(() => { + const page = this.get(pageId) + if (!page) { + throw new Error(`Report page with id "${pageId}" not found`) + } + callback(page) + }) + return this.parent + } + + get(pageId: string): ReportPageBuilder | undefined { + const pages = getOrCreateReportPages(this.dsl) + const index = locateReportPageIndexById(pages, pageId) + return index === -1 ? undefined : this.createPageBuilder(pages.get(index)) + } + + private createPageBuilder(page: Y.Map) { + return new ReportPageBuilder(this.doc, page, this.options?.chart) + } +} diff --git a/packages/vbi/src/report-builder/features/page/text-builder.ts b/packages/vbi/src/report-builder/features/page/text-builder.ts new file mode 100644 index 0000000000..168e8baa2e --- /dev/null +++ b/packages/vbi/src/report-builder/features/page/text-builder.ts @@ -0,0 +1,24 @@ +import * as Y from 'yjs' +import type { VBIReportTextDSL } from 'src/types' + +export class ReportTextBuilder { + constructor(private yMap: Y.Map) {} + + getContent(): string { + return this.yMap.get('content') ?? '' + } + + setContent(content: string): this { + this.yMap.set('content', content) + return this + } + + clear(): this { + this.yMap.set('content', '') + return this + } + + toJSON(): VBIReportTextDSL { + return this.yMap.toJSON() as VBIReportTextDSL + } +} diff --git a/packages/vbi/src/report-builder/index.ts b/packages/vbi/src/report-builder/index.ts new file mode 100644 index 0000000000..b1940ac8bd --- /dev/null +++ b/packages/vbi/src/report-builder/index.ts @@ -0,0 +1,2 @@ +export { VBIReportBuilder } from './builder' +export { ReportPageBuilder, ReportPageCollectionBuilder, ReportTextBuilder, UndoManager } from './features' diff --git a/packages/vbi/src/report-builder/modules/build.ts b/packages/vbi/src/report-builder/modules/build.ts new file mode 100644 index 0000000000..504dc5e39b --- /dev/null +++ b/packages/vbi/src/report-builder/modules/build.ts @@ -0,0 +1,7 @@ +import * as Y from 'yjs' +import type { VBIReportDSL } from 'src/types' +import { zVBIReportDSL } from 'src/types/reportDSL/report' + +export const buildVBIReportDSL = (dsl: Y.Map): VBIReportDSL => { + return zVBIReportDSL.parse(dsl.toJSON()) +} diff --git a/packages/vbi/src/report-builder/modules/index.ts b/packages/vbi/src/report-builder/modules/index.ts new file mode 100644 index 0000000000..02aed3256d --- /dev/null +++ b/packages/vbi/src/report-builder/modules/index.ts @@ -0,0 +1,4 @@ +export { applyUpdateToDoc } from 'src/chart-builder/modules/apply-update' +export { encodeDocStateAsUpdate } from 'src/chart-builder/modules/encode-state-as-update' +export { buildVBIReportDSL } from './build' +export { isEmptyVBIReportDSL } from './is-empty' diff --git a/packages/vbi/src/report-builder/modules/is-empty.ts b/packages/vbi/src/report-builder/modules/is-empty.ts new file mode 100644 index 0000000000..b18f3667bf --- /dev/null +++ b/packages/vbi/src/report-builder/modules/is-empty.ts @@ -0,0 +1,6 @@ +import * as Y from 'yjs' + +export const isEmptyVBIReportDSL = (dsl: Y.Map): boolean => { + const pages = dsl.get('pages') + return !(pages instanceof Y.Array) || pages.length === 0 +} diff --git a/packages/vbi/src/types/builder/VBIInterface.ts b/packages/vbi/src/types/builder/VBIInterface.ts index f0a23e0994..d90d52b596 100644 --- a/packages/vbi/src/types/builder/VBIInterface.ts +++ b/packages/vbi/src/types/builder/VBIInterface.ts @@ -1,5 +1,5 @@ -import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/builder/adapters/vquery-vseed/types' -import type { VBIChartDSL } from '../dsl' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' +import type { VBIChartDSL } from '../chartDSL' import type { BuildVSeedOptions } from './build-vseed' import type { MeasuresBuilder, @@ -11,7 +11,7 @@ import type { LocaleBuilder, LimitBuilder, UndoManager, -} from 'src/builder/features' +} from 'src/chart-builder/features' import type { Map, Doc } from 'yjs' export interface VBIChartBuilderInterface { diff --git a/packages/vbi/src/types/builder/adapter.ts b/packages/vbi/src/types/builder/adapter.ts index e914db0dc5..3a6320e747 100644 --- a/packages/vbi/src/types/builder/adapter.ts +++ b/packages/vbi/src/types/builder/adapter.ts @@ -1,6 +1,6 @@ -import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/builder/adapters/vquery-vseed/types' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' import type { Map } from 'yjs' -import type { VBIChartDSL } from '../dsl' +import type { VBIChartDSL } from '../chartDSL' import type { BuildVSeedOptions } from './build-vseed' import type { VBIChartBuilderInterface } from './VBIInterface' diff --git a/packages/vbi/src/types/builder/context.ts b/packages/vbi/src/types/builder/context.ts index e21605b824..f784b363f9 100644 --- a/packages/vbi/src/types/builder/context.ts +++ b/packages/vbi/src/types/builder/context.ts @@ -1,4 +1,4 @@ -import { VBIChartDSL } from '../dsl' +import { VBIChartDSL } from '../chartDSL' export interface BuilderContext { getVBIChartDSL(): VBIChartDSL diff --git a/packages/vbi/src/types/builder/index.ts b/packages/vbi/src/types/builder/index.ts index e2a45c02db..e8ebc3e193 100644 --- a/packages/vbi/src/types/builder/index.ts +++ b/packages/vbi/src/types/builder/index.ts @@ -8,4 +8,5 @@ export type { VBIChartBuilderAdapters, VBIChartBuilderOptions, } from './adapter' +export type { VBIReportBuilderInterface, VBIReportBuilderOptions } from './report' export type { ObserveCallback, ObserveDeepCallback } from './observe' diff --git a/packages/vbi/src/types/builder/report.ts b/packages/vbi/src/types/builder/report.ts new file mode 100644 index 0000000000..4771cfaeff --- /dev/null +++ b/packages/vbi/src/types/builder/report.ts @@ -0,0 +1,23 @@ +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' +import type { ReportPageCollectionBuilder } from 'src/report-builder/features/page' +import type { UndoManager } from 'src/chart-builder/features' +import type { Doc, Map } from 'yjs' +import type { VBIReportDSL } from '../reportDSL' +import type { VBIChartBuilderOptions } from './adapter' + +export interface VBIReportBuilderOptions { + chart?: VBIChartBuilderOptions +} + +export interface VBIReportBuilderInterface { + doc: Doc + dsl: Map + undoManager: UndoManager + page: ReportPageCollectionBuilder + + applyUpdate: (update: Uint8Array, origin?: any) => void + encodeStateAsUpdate: (targetStateVector?: Uint8Array) => Uint8Array + + build: () => VBIReportDSL + isEmpty: () => boolean +} diff --git a/packages/vbi/src/types/dsl/dimensions/aggregate.ts b/packages/vbi/src/types/chartDSL/dimensions/aggregate.ts similarity index 100% rename from packages/vbi/src/types/dsl/dimensions/aggregate.ts rename to packages/vbi/src/types/chartDSL/dimensions/aggregate.ts diff --git a/packages/vbi/src/types/dsl/dimensions/dimensions.ts b/packages/vbi/src/types/chartDSL/dimensions/dimensions.ts similarity index 100% rename from packages/vbi/src/types/dsl/dimensions/dimensions.ts rename to packages/vbi/src/types/chartDSL/dimensions/dimensions.ts diff --git a/packages/vbi/src/types/dsl/encoding.ts b/packages/vbi/src/types/chartDSL/encoding.ts similarity index 100% rename from packages/vbi/src/types/dsl/encoding.ts rename to packages/vbi/src/types/chartDSL/encoding.ts diff --git a/packages/vbi/src/types/dsl/havingFilter/having.ts b/packages/vbi/src/types/chartDSL/havingFilter/having.ts similarity index 100% rename from packages/vbi/src/types/dsl/havingFilter/having.ts rename to packages/vbi/src/types/chartDSL/havingFilter/having.ts diff --git a/packages/vbi/src/types/dsl/index.ts b/packages/vbi/src/types/chartDSL/index.ts similarity index 100% rename from packages/vbi/src/types/dsl/index.ts rename to packages/vbi/src/types/chartDSL/index.ts diff --git a/packages/vbi/src/types/dsl/locale/locale.ts b/packages/vbi/src/types/chartDSL/locale/locale.ts similarity index 100% rename from packages/vbi/src/types/dsl/locale/locale.ts rename to packages/vbi/src/types/chartDSL/locale/locale.ts diff --git a/packages/vbi/src/types/dsl/measures/aggregate.ts b/packages/vbi/src/types/chartDSL/measures/aggregate.ts similarity index 100% rename from packages/vbi/src/types/dsl/measures/aggregate.ts rename to packages/vbi/src/types/chartDSL/measures/aggregate.ts diff --git a/packages/vbi/src/types/dsl/measures/measures.ts b/packages/vbi/src/types/chartDSL/measures/measures.ts similarity index 100% rename from packages/vbi/src/types/dsl/measures/measures.ts rename to packages/vbi/src/types/chartDSL/measures/measures.ts diff --git a/packages/vbi/src/types/dsl/sort.ts b/packages/vbi/src/types/chartDSL/sort.ts similarity index 100% rename from packages/vbi/src/types/dsl/sort.ts rename to packages/vbi/src/types/chartDSL/sort.ts diff --git a/packages/vbi/src/types/dsl/theme/theme.ts b/packages/vbi/src/types/chartDSL/theme/theme.ts similarity index 100% rename from packages/vbi/src/types/dsl/theme/theme.ts rename to packages/vbi/src/types/chartDSL/theme/theme.ts diff --git a/packages/vbi/src/types/dsl/vbi/vbi.ts b/packages/vbi/src/types/chartDSL/vbi/vbi.ts similarity index 100% rename from packages/vbi/src/types/dsl/vbi/vbi.ts rename to packages/vbi/src/types/chartDSL/vbi/vbi.ts diff --git a/packages/vbi/src/types/dsl/whereFilter/date.ts b/packages/vbi/src/types/chartDSL/whereFilter/date.ts similarity index 100% rename from packages/vbi/src/types/dsl/whereFilter/date.ts rename to packages/vbi/src/types/chartDSL/whereFilter/date.ts diff --git a/packages/vbi/src/types/dsl/whereFilter/filters.ts b/packages/vbi/src/types/chartDSL/whereFilter/filters.ts similarity index 100% rename from packages/vbi/src/types/dsl/whereFilter/filters.ts rename to packages/vbi/src/types/chartDSL/whereFilter/filters.ts diff --git a/packages/vbi/src/types/index.ts b/packages/vbi/src/types/index.ts index 5ded67b181..176a260f07 100644 --- a/packages/vbi/src/types/index.ts +++ b/packages/vbi/src/types/index.ts @@ -1,3 +1,4 @@ -export * from './dsl' +export * from './chartDSL' +export * from './reportDSL' export * from './builder' export * from './connector' diff --git a/packages/vbi/src/types/reportDSL/index.ts b/packages/vbi/src/types/reportDSL/index.ts new file mode 100644 index 0000000000..2882249292 --- /dev/null +++ b/packages/vbi/src/types/reportDSL/index.ts @@ -0,0 +1,3 @@ +export type { VBIReportTextDSL, VBIReportTextDSLInput } from './text' +export type { VBIReportPageDSL, VBIReportPageDSLInput } from './page' +export type { VBIReportDSL, VBIReportDSLInput } from './report' diff --git a/packages/vbi/src/types/reportDSL/page.ts b/packages/vbi/src/types/reportDSL/page.ts new file mode 100644 index 0000000000..a1f7c236ed --- /dev/null +++ b/packages/vbi/src/types/reportDSL/page.ts @@ -0,0 +1,13 @@ +import { z } from 'zod' +import { zVBIChartDSL } from '../chartDSL/vbi/vbi' +import { zVBIReportTextDSL } from './text' + +export const zVBIReportPageDSL = z.object({ + id: z.string(), + title: z.string(), + chart: zVBIChartDSL, + text: zVBIReportTextDSL.optional().default({ content: '' }), +}) + +export type VBIReportPageDSLInput = z.input +export type VBIReportPageDSL = z.output diff --git a/packages/vbi/src/types/reportDSL/report.ts b/packages/vbi/src/types/reportDSL/report.ts new file mode 100644 index 0000000000..943d33509b --- /dev/null +++ b/packages/vbi/src/types/reportDSL/report.ts @@ -0,0 +1,10 @@ +import { z } from 'zod' +import { zVBIReportPageDSL } from './page' + +export const zVBIReportDSL = z.object({ + pages: z.array(zVBIReportPageDSL).optional().default([]), + version: z.number().int().min(0).optional().default(0), +}) + +export type VBIReportDSLInput = z.input +export type VBIReportDSL = z.output diff --git a/packages/vbi/src/types/reportDSL/text.ts b/packages/vbi/src/types/reportDSL/text.ts new file mode 100644 index 0000000000..7767348404 --- /dev/null +++ b/packages/vbi/src/types/reportDSL/text.ts @@ -0,0 +1,8 @@ +import { z } from 'zod' + +export const zVBIReportTextDSL = z.object({ + content: z.string().optional().default(''), +}) + +export type VBIReportTextDSLInput = z.input +export type VBIReportTextDSL = z.output diff --git a/packages/vbi/src/utils/filter-guards.ts b/packages/vbi/src/utils/filter-guards.ts index 87d88a9191..9589e6aa9d 100644 --- a/packages/vbi/src/utils/filter-guards.ts +++ b/packages/vbi/src/utils/filter-guards.ts @@ -5,7 +5,7 @@ import type { VBIHavingClause, VBIHavingFilter, VBIHavingGroup, -} from 'src/types/dsl' +} from 'src/types/chartDSL' export function isVBIFilter(clause: VBIWhereClause): clause is VBIWhereFilter { return 'field' in clause diff --git a/packages/vbi/src/vbi/create-vbi.ts b/packages/vbi/src/vbi/create-vbi.ts index 2f5be19979..ed6dc82785 100644 --- a/packages/vbi/src/vbi/create-vbi.ts +++ b/packages/vbi/src/vbi/create-vbi.ts @@ -1,19 +1,29 @@ -import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/builder/adapters/vquery-vseed/types' -import { connectorMap, getConnector, registerConnector } from 'src/builder/connector' -import type { VBIChartBuilder } from 'src/builder/builder' -import type { VBIChartDSLInput, VBIChartBuilderOptions } from 'src/types' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' +import { connectorMap, getConnector, registerConnector } from 'src/chart-builder/connector' +import type { VBIChartBuilder } from 'src/chart-builder/builder' +import type { VBIReportBuilder } from 'src/report-builder/builder' +import type { VBIChartDSLInput, VBIChartBuilderOptions, VBIReportBuilderOptions, VBIReportDSLInput } from 'src/types' import { createChartBuilderFromVBIChartDSLInput } from './from/from-vbi-dsl-input' +import { createReportBuilderFromVBIReportDSLInput } from './from/from-vbi-report-dsl-input' import { generateEmptyChartDSL } from './generate-empty-dsl' +import { generateEmptyReportDSL } from './generate-empty-report-dsl' +import { generateEmptyReportPageDSL } from './generate-empty-report-page-dsl' export interface VBIInstance { connectorMap: typeof connectorMap registerConnector: typeof registerConnector getConnector: typeof getConnector generateEmptyChartDSL: typeof generateEmptyChartDSL + generateEmptyReportDSL: typeof generateEmptyReportDSL + generateEmptyReportPageDSL: typeof generateEmptyReportPageDSL createChart: ( vbi: VBIChartDSLInput, builderOptions?: VBIChartBuilderOptions, ) => VBIChartBuilder + createReport: ( + report: VBIReportDSLInput, + builderOptions?: VBIReportBuilderOptions, + ) => VBIReportBuilder } const mergeBuilderOptions = ( @@ -38,6 +48,14 @@ const mergeBuilderOptions = ( + base?: VBIChartBuilderOptions, + overrides?: VBIReportBuilderOptions, +) => { + const chart = mergeBuilderOptions(base, overrides?.chart) + return chart ? { chart } : undefined +} + export function createVBI(): VBIInstance export function createVBI( defaultBuilderOptions: VBIChartBuilderOptions, @@ -48,12 +66,21 @@ export function createVBI) => { return createChartBuilderFromVBIChartDSLInput(vbi, mergeBuilderOptions(defaultBuilderOptions, builderOptions)) } + const createReport = (report: VBIReportDSLInput, builderOptions?: VBIReportBuilderOptions) => { + return createReportBuilderFromVBIReportDSLInput( + report, + mergeReportBuilderOptions(defaultBuilderOptions, builderOptions), + ) + } return { connectorMap, registerConnector, getConnector, generateEmptyChartDSL, + generateEmptyReportDSL, + generateEmptyReportPageDSL, createChart, + createReport, } } diff --git a/packages/vbi/src/vbi/from/fill-vbi-chart-dsl-map.ts b/packages/vbi/src/vbi/from/fill-vbi-chart-dsl-map.ts new file mode 100644 index 0000000000..5c11d9f3fe --- /dev/null +++ b/packages/vbi/src/vbi/from/fill-vbi-chart-dsl-map.ts @@ -0,0 +1,15 @@ +import * as Y from 'yjs' +import type { VBIChartDSLInput } from 'src/types' +import { ensureYArray } from '../normalize/ensure-y-array' +import { ensureWhereGroup } from '../normalize/ensure-where-group' +import { ensureHavingGroup } from '../normalize/ensure-having-group' +import { setBaseDSLFields } from './set-base-dsl-fields' + +export const fillVBIChartDSLMap = (dsl: Y.Map, vbi: VBIChartDSLInput) => { + dsl.clear() + setBaseDSLFields(dsl, vbi) + dsl.set('whereFilter', ensureWhereGroup(vbi.whereFilter)) + dsl.set('havingFilter', ensureHavingGroup(vbi.havingFilter)) + dsl.set('measures', ensureYArray(vbi.measures, 'field')) + dsl.set('dimensions', ensureYArray(vbi.dimensions, 'field')) +} diff --git a/packages/vbi/src/vbi/from/from-vbi-dsl-input.ts b/packages/vbi/src/vbi/from/from-vbi-dsl-input.ts index f16ad33676..755db83d71 100644 --- a/packages/vbi/src/vbi/from/from-vbi-dsl-input.ts +++ b/packages/vbi/src/vbi/from/from-vbi-dsl-input.ts @@ -1,11 +1,8 @@ import * as Y from 'yjs' -import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/builder/adapters/vquery-vseed/types' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' import type { VBIChartDSLInput, VBIChartBuilderOptions } from 'src/types' -import { VBIChartBuilder } from 'src/builder/builder' -import { ensureYArray } from '../normalize/ensure-y-array' -import { ensureWhereGroup } from '../normalize/ensure-where-group' -import { ensureHavingGroup } from '../normalize/ensure-having-group' -import { setBaseDSLFields } from './set-base-dsl-fields' +import { VBIChartBuilder } from 'src/chart-builder/builder' +import { fillVBIChartDSLMap } from './fill-vbi-chart-dsl-map' export const createChartBuilderFromVBIChartDSLInput = ( vbi: VBIChartDSLInput, @@ -15,12 +12,8 @@ export const createChartBuilderFromVBIChartDSLInput = { - setBaseDSLFields(dsl, vbi) - dsl.set('whereFilter', ensureWhereGroup(vbi.whereFilter)) - dsl.set('havingFilter', ensureHavingGroup(vbi.havingFilter)) - dsl.set('measures', ensureYArray(vbi.measures, 'field')) - dsl.set('dimensions', ensureYArray(vbi.dimensions, 'field')) + fillVBIChartDSLMap(dsl, vbi) }) - return new VBIChartBuilder(doc, options) + return new VBIChartBuilder(doc, options, dsl) } diff --git a/packages/vbi/src/vbi/from/from-vbi-report-dsl-input.ts b/packages/vbi/src/vbi/from/from-vbi-report-dsl-input.ts new file mode 100644 index 0000000000..3608c334e0 --- /dev/null +++ b/packages/vbi/src/vbi/from/from-vbi-report-dsl-input.ts @@ -0,0 +1,22 @@ +import * as Y from 'yjs' +import type { DefaultVBIQueryDSL, DefaultVBISeedDSL } from 'src/chart-builder/adapters/vquery-vseed/types' +import type { VBIReportDSLInput, VBIReportBuilderOptions } from 'src/types' +import { zVBIReportDSL } from 'src/types/reportDSL/report' +import { VBIReportBuilder } from 'src/report-builder/builder' +import { ensureReportPages } from './report-page-y-map' + +export const createReportBuilderFromVBIReportDSLInput = ( + report: VBIReportDSLInput, + options?: VBIReportBuilderOptions, +) => { + const doc = new Y.Doc() + const dsl = doc.getMap('dsl') + const normalized = zVBIReportDSL.parse(report) + + doc.transact(() => { + dsl.set('version', normalized.version) + dsl.set('pages', ensureReportPages(normalized.pages)) + }) + + return new VBIReportBuilder(doc, options) +} diff --git a/packages/vbi/src/vbi/from/report-page-y-map.ts b/packages/vbi/src/vbi/from/report-page-y-map.ts new file mode 100644 index 0000000000..44952af871 --- /dev/null +++ b/packages/vbi/src/vbi/from/report-page-y-map.ts @@ -0,0 +1,69 @@ +import * as Y from 'yjs' +import type { VBIReportPageDSLInput, VBIReportTextDSLInput } from 'src/types' +import { id } from 'src/utils' +import { generateEmptyChartDSL } from '../generate-empty-dsl' +import { fillVBIChartDSLMap } from './fill-vbi-chart-dsl-map' + +const createReportTextYMap = (text?: VBIReportTextDSLInput) => { + const yMap = new Y.Map() + yMap.set('content', text?.content ?? '') + return yMap +} + +export const createReportPageYMap = (page: VBIReportPageDSLInput): Y.Map => { + const yMap = new Y.Map() + const chart = new Y.Map() + + yMap.set('id', page.id || id.uuid()) + yMap.set('title', page.title) + fillVBIChartDSLMap(chart, page.chart) + yMap.set('chart', chart) + yMap.set('text', createReportTextYMap(page.text)) + return yMap +} + +export const ensureReportPages = (pages?: VBIReportPageDSLInput[]) => { + const yArray = new Y.Array() + for (const page of pages ?? []) { + yArray.push([createReportPageYMap(page)]) + } + return yArray +} + +export const getOrCreateReportPages = (dsl: Y.Map) => { + const pages = dsl.get('pages') + if (pages instanceof Y.Array) { + return pages as Y.Array> + } + + const nextPages = new Y.Array>() + dsl.set('pages', nextPages) + return nextPages +} + +export const getOrCreateReportChartMap = (page: Y.Map) => { + const chart = page.get('chart') + if (chart instanceof Y.Map) { + return chart as Y.Map + } + + const nextChart = new Y.Map() + fillVBIChartDSLMap(nextChart, generateEmptyChartDSL('')) + page.set('chart', nextChart) + return nextChart +} + +export const getOrCreateReportTextMap = (page: Y.Map) => { + const text = page.get('text') + if (text instanceof Y.Map) { + return text as Y.Map + } + + const nextText = createReportTextYMap() + page.set('text', nextText) + return nextText +} + +export const locateReportPageIndexById = (pages: Y.Array>, pageId: string) => { + return pages.toArray().findIndex((page) => page.get('id') === pageId) +} diff --git a/packages/vbi/src/vbi/from/set-base-dsl-fields.ts b/packages/vbi/src/vbi/from/set-base-dsl-fields.ts index c6bd27bef9..463717d75b 100644 --- a/packages/vbi/src/vbi/from/set-base-dsl-fields.ts +++ b/packages/vbi/src/vbi/from/set-base-dsl-fields.ts @@ -2,10 +2,10 @@ import * as Y from 'yjs' import type { VBIChartDSLInput } from 'src/types' export const setBaseDSLFields = (dsl: Y.Map, vbi: VBIChartDSLInput) => { - if (vbi.connectorId) dsl.set('connectorId', vbi.connectorId) - if (vbi.chartType) dsl.set('chartType', vbi.chartType) - if (vbi.theme) dsl.set('theme', vbi.theme) - if (vbi.limit) dsl.set('limit', vbi.limit) - if (vbi.locale) dsl.set('locale', vbi.locale) + if (vbi.connectorId !== undefined) dsl.set('connectorId', vbi.connectorId) + if (vbi.chartType !== undefined) dsl.set('chartType', vbi.chartType) + if (vbi.theme !== undefined) dsl.set('theme', vbi.theme) + if (vbi.limit !== undefined) dsl.set('limit', vbi.limit) + if (vbi.locale !== undefined) dsl.set('locale', vbi.locale) if (vbi.version !== undefined) dsl.set('version', vbi.version) } diff --git a/packages/vbi/src/vbi/generate-empty-report-dsl.ts b/packages/vbi/src/vbi/generate-empty-report-dsl.ts new file mode 100644 index 0000000000..4482a2087a --- /dev/null +++ b/packages/vbi/src/vbi/generate-empty-report-dsl.ts @@ -0,0 +1,8 @@ +import type { VBIReportDSL } from 'src/types' + +export const generateEmptyReportDSL = (): VBIReportDSL => { + return { + pages: [], + version: 0, + } +} diff --git a/packages/vbi/src/vbi/generate-empty-report-page-dsl.ts b/packages/vbi/src/vbi/generate-empty-report-page-dsl.ts new file mode 100644 index 0000000000..1bd994490a --- /dev/null +++ b/packages/vbi/src/vbi/generate-empty-report-page-dsl.ts @@ -0,0 +1,15 @@ +import type { VBIConnectorId } from 'src/types/connector/connector' +import type { VBIReportPageDSL } from 'src/types' +import { id } from 'src/utils' +import { generateEmptyChartDSL } from './generate-empty-dsl' + +export const generateEmptyReportPageDSL = (connectorId: VBIConnectorId = ''): VBIReportPageDSL => { + return { + id: id.uuid(), + title: '', + chart: generateEmptyChartDSL(connectorId), + text: { + content: '', + }, + } +} diff --git a/packages/vbi/src/vbi/normalize/ensure-having-group.ts b/packages/vbi/src/vbi/normalize/ensure-having-group.ts index f25f534447..8b6801b406 100644 --- a/packages/vbi/src/vbi/normalize/ensure-having-group.ts +++ b/packages/vbi/src/vbi/normalize/ensure-having-group.ts @@ -1,5 +1,5 @@ import * as Y from 'yjs' -import { createHavingGroup } from 'src/builder/features/havingFilter/having-utils' +import { createHavingGroup } from 'src/chart-builder/features/havingFilter/having-utils' import { ensureYArray } from './ensure-y-array' import type { FilterGroupInput } from './types' diff --git a/packages/vbi/src/vbi/normalize/ensure-where-group.ts b/packages/vbi/src/vbi/normalize/ensure-where-group.ts index b266627d48..5ad0ccc68d 100644 --- a/packages/vbi/src/vbi/normalize/ensure-where-group.ts +++ b/packages/vbi/src/vbi/normalize/ensure-where-group.ts @@ -1,5 +1,5 @@ import * as Y from 'yjs' -import { createWhereGroup } from 'src/builder/features/whereFilter/where-utils' +import { createWhereGroup } from 'src/chart-builder/features/whereFilter/where-utils' import { ensureYArray } from './ensure-y-array' import type { FilterGroupInput } from './types' diff --git a/packages/vbi/tests/builder/builder.test.ts b/packages/vbi/tests/builder/builder.test.ts index 5509ab47aa..f92d1ec201 100644 --- a/packages/vbi/tests/builder/builder.test.ts +++ b/packages/vbi/tests/builder/builder.test.ts @@ -1,6 +1,6 @@ import { createVBI, VBI } from '@visactor/vbi' -import { VBIChartDSL } from 'src/types/dsl' -import { getConnector, registerConnector } from 'src/builder/connector' +import { VBIChartDSL } from 'src/types/chartDSL' +import { getConnector, registerConnector } from 'src/chart-builder/connector' describe('VBI', () => { test('build', () => { diff --git a/packages/vbi/tests/builder/features/chartType.test.ts b/packages/vbi/tests/builder/features/chartType.test.ts index a98b962b29..1739f07416 100644 --- a/packages/vbi/tests/builder/features/chartType.test.ts +++ b/packages/vbi/tests/builder/features/chartType.test.ts @@ -1,5 +1,5 @@ import { VBI } from '@visactor/vbi' -import { VBIChartDSL } from 'src/types/dsl' +import { VBIChartDSL } from 'src/types/chartDSL' describe('ChartTypeBuilder', () => { test('changeChartType', () => { diff --git a/packages/vbi/tests/builder/features/configFeatures.test.ts b/packages/vbi/tests/builder/features/configFeatures.test.ts index 6ee8d3e01c..2bf3aea7b8 100644 --- a/packages/vbi/tests/builder/features/configFeatures.test.ts +++ b/packages/vbi/tests/builder/features/configFeatures.test.ts @@ -1,15 +1,15 @@ import { VBI } from '@visactor/vbi' -import * as features from 'src/builder/features' -import { ChartTypeBuilder } from 'src/builder/features/chart-type' -import { DimensionsBuilder } from 'src/builder/features/dimensions' -import { HavingFilterBuilder } from 'src/builder/features/havingFilter' -import { LimitBuilder } from 'src/builder/features/limit' -import { LocaleBuilder } from 'src/builder/features/locale' -import { MeasuresBuilder } from 'src/builder/features/measures' -import { ThemeBuilder } from 'src/builder/features/theme' -import { UndoManager } from 'src/builder/features/undo-manager' -import { WhereFilterBuilder } from 'src/builder/features/whereFilter' -import type { VBIChartDSL } from 'src/types/dsl' +import * as features from 'src/chart-builder/features' +import { ChartTypeBuilder } from 'src/chart-builder/features/chart-type' +import { DimensionsBuilder } from 'src/chart-builder/features/dimensions' +import { HavingFilterBuilder } from 'src/chart-builder/features/havingFilter' +import { LimitBuilder } from 'src/chart-builder/features/limit' +import { LocaleBuilder } from 'src/chart-builder/features/locale' +import { MeasuresBuilder } from 'src/chart-builder/features/measures' +import { ThemeBuilder } from 'src/chart-builder/features/theme' +import { UndoManager } from 'src/chart-builder/features/undo-manager' +import { WhereFilterBuilder } from 'src/chart-builder/features/whereFilter' +import type { VBIChartDSL } from 'src/types/chartDSL' describe('feature barrels', () => { test('export all feature builders from the root barrel', () => { diff --git a/packages/vbi/tests/builder/features/dimensions.test.ts b/packages/vbi/tests/builder/features/dimensions.test.ts index 051c4040a0..4ca228c0d1 100644 --- a/packages/vbi/tests/builder/features/dimensions.test.ts +++ b/packages/vbi/tests/builder/features/dimensions.test.ts @@ -1,6 +1,6 @@ import { VBI } from '@visactor/vbi' -import { VBIChartDSL } from 'src/types/dsl' -import { DimensionsBuilder } from 'src/builder/features/dimensions/dim-builder' +import { VBIChartDSL } from 'src/types/chartDSL' +import { DimensionsBuilder } from 'src/chart-builder/features/dimensions/dim-builder' describe('DimensionsBuilder', () => { test('addDimension', () => { diff --git a/packages/vbi/tests/builder/features/filterCoverage.test.ts b/packages/vbi/tests/builder/features/filterCoverage.test.ts index 5c8ca274e5..ba7ab43136 100644 --- a/packages/vbi/tests/builder/features/filterCoverage.test.ts +++ b/packages/vbi/tests/builder/features/filterCoverage.test.ts @@ -1,18 +1,18 @@ import * as Y from 'yjs' import { VBI } from '@visactor/vbi' -import { HavingFilterBuilder } from 'src/builder/features/havingFilter/having-builder' +import { HavingFilterBuilder } from 'src/chart-builder/features/havingFilter/having-builder' import { createHavingGroup, findEntry as findHavingEntry, isHavingGroup, -} from 'src/builder/features/havingFilter/having-utils' -import { WhereFilterBuilder } from 'src/builder/features/whereFilter/where-builder' +} from 'src/chart-builder/features/havingFilter/having-utils' +import { WhereFilterBuilder } from 'src/chart-builder/features/whereFilter/where-builder' import { createWhereGroup, findEntry as findWhereEntry, isWhereGroup, -} from 'src/builder/features/whereFilter/where-utils' -import type { VBIChartDSL } from 'src/types/dsl' +} from 'src/chart-builder/features/whereFilter/where-utils' +import type { VBIChartDSL } from 'src/types/chartDSL' describe('Where filter internals', () => { test('constructor initializes a missing whereFilter root on plain Y DSL', () => { diff --git a/packages/vbi/tests/builder/features/havingFilter.test.ts b/packages/vbi/tests/builder/features/havingFilter.test.ts index 96e1a3994e..e815ca8139 100644 --- a/packages/vbi/tests/builder/features/havingFilter.test.ts +++ b/packages/vbi/tests/builder/features/havingFilter.test.ts @@ -1,9 +1,9 @@ import * as Y from 'yjs' import { VBI } from '@visactor/vbi' -import { VBIChartDSL } from 'src/types/dsl' -import { HavingFilterNodeBuilder } from 'src/builder/features/havingFilter/having-node-builder' -import { HavingGroupBuilder } from 'src/builder/features/havingFilter/having-group-builder' -import { HavingFilterBuilder } from 'src/builder/features/havingFilter/having-builder' +import { VBIChartDSL } from 'src/types/chartDSL' +import { HavingFilterNodeBuilder } from 'src/chart-builder/features/havingFilter/having-node-builder' +import { HavingGroupBuilder } from 'src/chart-builder/features/havingFilter/having-group-builder' +import { HavingFilterBuilder } from 'src/chart-builder/features/havingFilter/having-builder' describe('HavingFilterBuilder', () => { test('add having filter', () => { diff --git a/packages/vbi/tests/builder/features/measures.test.ts b/packages/vbi/tests/builder/features/measures.test.ts index e33a1a383d..866a4531fe 100644 --- a/packages/vbi/tests/builder/features/measures.test.ts +++ b/packages/vbi/tests/builder/features/measures.test.ts @@ -1,6 +1,6 @@ import { VBI } from '@visactor/vbi' -import { VBIChartDSL } from 'src/types/dsl' -import { MeasuresBuilder } from 'src/builder/features/measures/mea-builder' +import { VBIChartDSL } from 'src/types/chartDSL' +import { MeasuresBuilder } from 'src/chart-builder/features/measures/mea-builder' import { registerDemoConnector } from '../../demoConnector' describe('MeasuresBuilder', () => { diff --git a/packages/vbi/tests/builder/features/sort.test.ts b/packages/vbi/tests/builder/features/sort.test.ts index fc042e37c7..d12ab52d42 100644 --- a/packages/vbi/tests/builder/features/sort.test.ts +++ b/packages/vbi/tests/builder/features/sort.test.ts @@ -1,5 +1,5 @@ import { VBI } from '@visactor/vbi' -import type { VBIChartDSL } from 'src/types/dsl' +import type { VBIChartDSL } from 'src/types/chartDSL' describe('sort builders', () => { test('DimensionNodeBuilder set/get/clearSort works', () => { diff --git a/packages/vbi/tests/builder/features/whereFilter.test.ts b/packages/vbi/tests/builder/features/whereFilter.test.ts index c4c502d82f..66dd0dca5c 100644 --- a/packages/vbi/tests/builder/features/whereFilter.test.ts +++ b/packages/vbi/tests/builder/features/whereFilter.test.ts @@ -1,6 +1,6 @@ import * as Y from 'yjs' import { VBI } from '@visactor/vbi' -import type { VBIChartDSL, VBIWhereFilter } from 'src/types/dsl' +import type { VBIChartDSL, VBIWhereFilter } from 'src/types/chartDSL' describe('WhereFilterBuilder', () => { test('addWhereFilter', () => { diff --git a/packages/vbi/tests/builder/filters.test.ts b/packages/vbi/tests/builder/filters.test.ts index 785956eeed..2655f75665 100644 --- a/packages/vbi/tests/builder/filters.test.ts +++ b/packages/vbi/tests/builder/filters.test.ts @@ -1,5 +1,5 @@ import { VBI } from '@visactor/vbi' -import type { VBIChartDSL, VBIWhereFilter } from 'src/types/dsl' +import type { VBIChartDSL, VBIWhereFilter } from 'src/types/chartDSL' describe('WhereFilterBuilder', () => { test('add', () => { diff --git a/packages/vbi/tests/builder/reportBuilder.test.ts b/packages/vbi/tests/builder/reportBuilder.test.ts new file mode 100644 index 0000000000..57abca5d68 --- /dev/null +++ b/packages/vbi/tests/builder/reportBuilder.test.ts @@ -0,0 +1,109 @@ +import { createVBI, VBI } from '@visactor/vbi' + +describe('VBIReportBuilder', () => { + test('page.add builds report from chart builder and text', () => { + const chartBuilder = VBI.createChart(VBI.generateEmptyChartDSL('demo')) + chartBuilder.measures.add('sales', (node) => { + node.setAlias('Sales').setAggregate({ func: 'sum' }).setEncoding('yAxis') + }) + + const reportBuilder = VBI.createReport(VBI.generateEmptyReportDSL()) + const report = reportBuilder.page + .add('Story One', (page) => page.setChart(chartBuilder).setText('hello world')) + .build() + + expect(report).toEqual({ + pages: [ + { + id: 'id-2', + title: 'Story One', + chart: { + connectorId: 'demo', + chartType: 'table', + measures: [ + { + id: 'id-1', + aggregate: { func: 'sum' }, + alias: 'Sales', + encoding: 'yAxis', + field: 'sales', + }, + ], + dimensions: [], + whereFilter: { id: 'root', op: 'and', conditions: [] }, + havingFilter: { id: 'root', op: 'and', conditions: [] }, + theme: 'light', + locale: 'zh-CN', + version: 0, + }, + text: { content: 'hello world' }, + }, + ], + version: 0, + }) + }) + + test('page.update and page.remove work on existing pages', () => { + const reportBuilder = VBI.createReport(VBI.generateEmptyReportDSL()) + const pageId = reportBuilder.page.add('Story One').build().pages[0].id + + reportBuilder.page.update(pageId, (page) => { + page.setTitle('Story Two').setText('updated') + }) + + expect(reportBuilder.build().pages[0]).toMatchObject({ + id: pageId, + title: 'Story Two', + text: { content: 'updated' }, + }) + + reportBuilder.page.remove(pageId) + expect(reportBuilder.isEmpty()).toBe(true) + }) + + test('setChart copies chart DSL instead of sharing builder state', () => { + const chartBuilder = VBI.createChart(VBI.generateEmptyChartDSL('demo')) + const reportBuilder = VBI.createReport(VBI.generateEmptyReportDSL()) + const pageId = reportBuilder.page.add('Story One', (page) => page.setChart(chartBuilder)).build().pages[0].id + + chartBuilder.dimensions.add('area', (node) => { + node.setAlias('Area') + }) + + expect(reportBuilder.page.get(pageId)?.chart.build().dimensions).toEqual([]) + }) + + test('createReport uses default chart builder options from createVBI', () => { + type CustomQueryDSL = { source: 'factory'; count: number } + type CustomSeedDSL = { type: 'custom-seed'; queryDSL: CustomQueryDSL } + + const CustomVBI = createVBI({ + adapters: { + buildVQuery: ({ vbiDSL }) => ({ source: 'factory', count: vbiDSL.measures.length }), + buildVSeed: async ({ queryDSL }) => ({ type: 'custom-seed', queryDSL }), + }, + }) + + const chartBuilder = CustomVBI.createChart(CustomVBI.generateEmptyChartDSL('demo')) + const reportBuilder = CustomVBI.createReport(CustomVBI.generateEmptyReportDSL()) + const pageId = reportBuilder.page.add('Story One', (page) => page.setChart(chartBuilder)).build().pages[0].id + + expect(reportBuilder.page.get(pageId)?.chart.buildVQuery()).toEqual({ + source: 'factory', + count: 0, + }) + }) + + test('report builders sync through YJS updates', () => { + const b1 = VBI.createReport(VBI.generateEmptyReportDSL()) + const b2 = VBI.createReport(VBI.generateEmptyReportDSL()) + + b2.applyUpdate(b1.encodeStateAsUpdate()) + b1.applyUpdate(b2.encodeStateAsUpdate()) + + b1.page.add('Story One', (page) => page.setText('synced')) + b2.applyUpdate(b1.encodeStateAsUpdate()) + + expect(b2.build()).toEqual(b1.build()) + }) +}) diff --git a/packages/vbi/tests/builder/undo-manager.test.ts b/packages/vbi/tests/builder/undo-manager.test.ts index 23c2db44ef..65310deae8 100644 --- a/packages/vbi/tests/builder/undo-manager.test.ts +++ b/packages/vbi/tests/builder/undo-manager.test.ts @@ -1,5 +1,5 @@ import { VBI } from '@visactor/vbi' -import { VBIChartDSL } from 'src/types/dsl' +import { VBIChartDSL } from 'src/types/chartDSL' describe('UndoManager', () => { test('basic undo/redo', () => { diff --git a/packages/vbi/tests/builder/yjs.test.ts b/packages/vbi/tests/builder/yjs.test.ts index eb6360c482..8f5305e694 100644 --- a/packages/vbi/tests/builder/yjs.test.ts +++ b/packages/vbi/tests/builder/yjs.test.ts @@ -1,5 +1,5 @@ import { VBI } from '@visactor/vbi' -import { VBIChartDSL } from 'src/types/dsl' +import { VBIChartDSL } from 'src/types/chartDSL' describe('VBI YJS Integration', () => { test('sync between two builders', () => { diff --git a/packages/vbi/tests/examples/whereFilter/whereFilter.test.ts b/packages/vbi/tests/examples/whereFilter/whereFilter.test.ts index eb6c39d90b..737becda99 100644 --- a/packages/vbi/tests/examples/whereFilter/whereFilter.test.ts +++ b/packages/vbi/tests/examples/whereFilter/whereFilter.test.ts @@ -1988,12 +1988,12 @@ describe('WhereFilter', () => { { "field": "order_date", "op": ">=", - "value": "2026-02-21", + "value": "2026-02-22", }, { "field": "order_date", "op": "<", - "value": "2026-03-23", + "value": "2026-03-24", }, { "field": "sales", diff --git a/packages/vbi/tests/query/orderBy.test.ts b/packages/vbi/tests/query/orderBy.test.ts index c47d9ea452..2dd559f322 100644 --- a/packages/vbi/tests/query/orderBy.test.ts +++ b/packages/vbi/tests/query/orderBy.test.ts @@ -1,5 +1,5 @@ import { VBI } from '@visactor/vbi' -import type { VBIChartDSL } from 'src/types/dsl' +import type { VBIChartDSL } from 'src/types/chartDSL' describe('orderBy', () => { test('defaults to the first dimension when no explicit sort exists', () => { diff --git a/packages/vbi/tests/types/dateFilterSchemas.test.ts b/packages/vbi/tests/types/dateFilterSchemas.test.ts index b9f989f4dd..a79fe62d6c 100644 --- a/packages/vbi/tests/types/dateFilterSchemas.test.ts +++ b/packages/vbi/tests/types/dateFilterSchemas.test.ts @@ -3,7 +3,7 @@ import { zVBIWhereFilter, zVBIWhereDateFilter, zVBIWhereScalarFilter, -} from 'src/types/dsl/whereFilter/filters' +} from 'src/types/chartDSL/whereFilter/filters' describe('VBIWhereDatePredicate schema', () => { test('parse range predicate', () => { diff --git a/packages/vbi/tests/types/reportSchemas.test.ts b/packages/vbi/tests/types/reportSchemas.test.ts new file mode 100644 index 0000000000..fa19878f10 --- /dev/null +++ b/packages/vbi/tests/types/reportSchemas.test.ts @@ -0,0 +1,59 @@ +import { zVBIReportDSL } from 'src/types/reportDSL/report' +import { generateEmptyReportDSL } from 'src/vbi/generate-empty-report-dsl' +import { generateEmptyReportPageDSL } from 'src/vbi/generate-empty-report-page-dsl' + +describe('report DSL schemas', () => { + test('parse minimal report DSL', () => { + expect(zVBIReportDSL.parse(generateEmptyReportDSL())).toEqual({ + pages: [], + version: 0, + }) + }) + + test('page requires title, chart and text', () => { + const page = generateEmptyReportPageDSL('demo') + const withoutTitle: Partial = { ...page } + const withoutChart: Partial = { ...page } + const withoutText: Partial = { ...page } + + delete withoutTitle.title + delete withoutChart.chart + delete withoutText.text + + expect(() => zVBIReportDSL.parse({ pages: [withoutTitle], version: 0 })).toThrow() + expect(() => zVBIReportDSL.parse({ pages: [withoutChart], version: 0 })).toThrow() + expect(zVBIReportDSL.parse({ pages: [withoutText], version: 0 }).pages[0].text).toEqual({ content: '' }) + }) + + test('page.chart reuses chart schema validation', () => { + const page = generateEmptyReportPageDSL('demo') + + expect(() => + zVBIReportDSL.parse({ + pages: [{ ...page, chart: { ...page.chart, connectorId: 1 } }], + version: 0, + }), + ).toThrow() + }) + + test('empty report helpers stay stable', () => { + expect(generateEmptyReportPageDSL('demo')).toEqual({ + id: 'id-1', + title: '', + chart: { + connectorId: 'demo', + chartType: 'table', + measures: [], + dimensions: [], + whereFilter: { id: 'root', op: 'and', conditions: [] }, + havingFilter: { id: 'root', op: 'and', conditions: [] }, + theme: 'light', + locale: 'zh-CN', + version: 0, + }, + text: { + content: '', + }, + }) + }) +}) diff --git a/packages/vbi/tests/types/runtimeSchemas.test.ts b/packages/vbi/tests/types/runtimeSchemas.test.ts index 1c002767c3..8c26e9d65b 100644 --- a/packages/vbi/tests/types/runtimeSchemas.test.ts +++ b/packages/vbi/tests/types/runtimeSchemas.test.ts @@ -1,12 +1,12 @@ -import { zDimensionAggregate } from 'src/types/dsl/dimensions/aggregate' -import { zVBIDimensionGroupSchema, zVBIDimensionTree } from 'src/types/dsl/dimensions/dimensions' -import { zVBIHavingClause, zVBIHavingFilter, zVBIHavingGroup } from 'src/types/dsl/havingFilter/having' -import { zVBIDSLLocale } from 'src/types/dsl/locale/locale' -import { zAggregate } from 'src/types/dsl/measures/aggregate' -import { zVBIMeasure, zVBIMeasureGroup, zVBIMeasureTree } from 'src/types/dsl/measures/measures' -import { zVBIDSLTheme } from 'src/types/dsl/theme/theme' -import { zVBIChartDSL } from 'src/types/dsl/vbi/vbi' -import { zVBIWhereFilter, zVBIWhereClause, zVBIWhereGroup } from 'src/types/dsl/whereFilter/filters' +import { zDimensionAggregate } from 'src/types/chartDSL/dimensions/aggregate' +import { zVBIDimensionGroupSchema, zVBIDimensionTree } from 'src/types/chartDSL/dimensions/dimensions' +import { zVBIHavingClause, zVBIHavingFilter, zVBIHavingGroup } from 'src/types/chartDSL/havingFilter/having' +import { zVBIDSLLocale } from 'src/types/chartDSL/locale/locale' +import { zAggregate } from 'src/types/chartDSL/measures/aggregate' +import { zVBIMeasure, zVBIMeasureGroup, zVBIMeasureTree } from 'src/types/chartDSL/measures/measures' +import { zVBIDSLTheme } from 'src/types/chartDSL/theme/theme' +import { zVBIChartDSL } from 'src/types/chartDSL/vbi/vbi' +import { zVBIWhereFilter, zVBIWhereClause, zVBIWhereGroup } from 'src/types/chartDSL/whereFilter/filters' import { findTreeNodesBy, id, diff --git a/packages/vbi/tests/types/sortSchemas.test.ts b/packages/vbi/tests/types/sortSchemas.test.ts index 6d8118cdc5..11e04eaca2 100644 --- a/packages/vbi/tests/types/sortSchemas.test.ts +++ b/packages/vbi/tests/types/sortSchemas.test.ts @@ -1,7 +1,7 @@ -import { zVBIDimensionSchema } from 'src/types/dsl/dimensions/dimensions' -import { zVBIMeasure } from 'src/types/dsl/measures/measures' -import { zVBISort } from 'src/types/dsl/sort' -import { zVBIChartDSL } from 'src/types/dsl/vbi/vbi' +import { zVBIDimensionSchema } from 'src/types/chartDSL/dimensions/dimensions' +import { zVBIMeasure } from 'src/types/chartDSL/measures/measures' +import { zVBISort } from 'src/types/chartDSL/sort' +import { zVBIChartDSL } from 'src/types/chartDSL/vbi/vbi' describe('sort schemas', () => { test('zVBISort parses asc and desc', () => { From fad06947f85c03824ea434feecebb84ccb0ddf8f Mon Sep 17 00:00:00 2001 From: "jiangtao.young" Date: Tue, 24 Mar 2026 21:39:12 +0800 Subject: [PATCH 2/5] feat: refactor demo practice to standard and add standard-report practice - Rename practices/demo to practices/standard (chart builder) - Add practices/standard-report (report builder with multi-page support) - Update documentation and configuration files - Move todo3-create-report docs to dated folder structure Co-Authored-By: Claude Opus 4.6 --- AGENTS.md | 2 +- CLAUDE.md | 2 +- apps/vbi_fe/package.json | 2 +- apps/vbi_fe/src/pages/DocumentEditorPage.tsx | 2 +- apps/vbi_fe/tsconfig.json | 2 +- .../docs/zh-CN/vbi/practices/_meta.json | 1 + .../zh-CN/vbi/practices/standard-report.mdx | 7 + .../docs/zh-CN/vbi/practices/standard.mdx | 2 +- apps/website/package.json | 3 +- apps/website/tsconfig.json | 5 +- packages/vbi-react/DESIGN.md | 36 +-- .../vbi/docs/2026-03-19-measure-format/adr.md | 14 +- .../docs/2026-03-19-measure-format/plan.md | 2 +- .../adr.md | 48 ++-- .../plan.md | 2 +- .../vbi/docs/2026-03-23-mea-dim-sort/plan.md | 2 +- .../adr.md | 0 .../goal.md | 0 .../plan.md | 0 pnpm-lock.yaml | 209 ++++++++++++------ practices/standard-report/.gitignore | 15 ++ .../{demo => standard-report}/.prettierignore | 0 .../{demo => standard-report}/.prettierrc | 0 practices/standard-report/AGENTS.md | 28 +++ practices/standard-report/README.md | 23 ++ .../docs/2026-03-24-standard-report/adr.md | 182 +++++++++++++++ .../docs/2026-03-24-standard-report}/goal.md | 2 +- .../docs/2026-03-24-standard-report/plan.md | 133 +++++++++++ practices/standard-report/docs/README.md | 7 + .../eslint.config.mjs | 0 practices/standard-report/package.json | 55 +++++ .../{demo => standard-report}/rslib.config.ts | 0 .../rstest.config.ts | 0 .../{demo => standard-report}/rstest.setup.ts | 0 practices/standard-report/src/App/App.tsx | 25 +++ .../components/editor/ReportEditorDrawer.tsx | 52 +++++ .../App/components/page/PageHoverActions.tsx | 69 ++++++ .../src/App/components/page/PageInsight.tsx | 10 + .../App/components/page/PagePreviewCanvas.tsx | 46 ++++ .../App/components/page/PagePreviewCard.tsx | 76 +++++++ .../App/components/stage/ReportCarousel.tsx | 66 ++++++ .../App/components/stage/StageNavButton.tsx | 31 +++ .../src/App/hooks/useCarouselDisplayIndex.ts | 26 +++ .../src/App/hooks/usePagePreview.ts | 56 +++++ .../src/App/index.ts | 0 .../src/App/layout/ReportStage.tsx | 26 +++ .../src/App/layout/ReportWorkbench.tsx | 26 +++ .../src/App/layout/view-config.ts | 40 ++++ .../standard-report/src/App/styles/editor.css | 59 +++++ .../standard-report/src/App/styles/index.css | 7 + .../src/App/styles/insight.css | 37 ++++ .../standard-report/src/App/styles/layout.css | 17 ++ .../src/App/styles/page-actions.css | 111 ++++++++++ .../standard-report/src/App/styles/page.css | 52 +++++ .../src/App/styles/placeholder.css | 42 ++++ .../standard-report/src/App/styles/stage.css | 52 +++++ .../standard-report/src/constants/builder.ts | 8 + practices/standard-report/src/index.tsx | 1 + practices/standard-report/src/model/index.ts | 2 + .../standard-report/src/model/report-store.ts | 107 +++++++++ .../src/utils/demoConnector.ts | 75 +++++++ .../standard-report/src/utils/report-pages.ts | 48 ++++ .../tests/page-hover-actions.test.tsx | 51 +++++ .../tests/report-pages.test.ts | 40 ++++ .../{demo => standard-report}/tests/test.d.ts | 0 practices/standard-report/tsconfig.json | 38 ++++ .../tsconfig.test.json | 0 practices/{demo => standard}/.gitignore | 0 practices/standard/.prettierignore | 4 + practices/standard/.prettierrc | 3 + practices/{demo => standard}/AGENTS.md | 0 practices/{demo => standard}/README.md | 0 .../docs/2026-03-20-shelves/adr.md | 38 ++-- .../docs/2026-03-20-shelves/goal.md | 2 +- practices/{demo => standard}/docs/README.md | 4 +- .../docs/todo1-upload-csv/goal.md | 2 +- practices/standard/eslint.config.mjs | 21 ++ practices/{demo => standard}/package.json | 2 +- practices/standard/rslib.config.ts | 21 ++ practices/standard/rstest.config.ts | 8 + practices/standard/rstest.setup.ts | 4 + practices/{demo => standard}/src/App/App.tsx | 0 practices/{demo => standard}/src/App/app.css | 0 .../src/App/components/ChartPanel.tsx | 0 .../src/App/components/FieldsPanel.tsx | 0 .../components/FilterRootOperatorToggle.tsx | 0 .../src/App/components/ShelfPanel.tsx | 0 .../src/App/components/ShelfRow.tsx | 0 .../src/App/components/index.ts | 0 practices/standard/src/App/index.ts | 1 + .../src/components/ChartType/Selector.tsx | 0 .../src/components/ChartType/index.ts | 0 .../Fields/DimensionsList/index.tsx | 0 .../src/components/Fields/FieldList/index.tsx | 0 .../components/Fields/MeasuresList/index.tsx | 0 .../components/Filter/DateFilterEditor.tsx | 0 .../src/components/Filter/FilterPanel.tsx | 0 .../components/Filter/HavingFilterPanel.tsx | 0 .../src/components/Filter/dateFilterUtils.ts | 0 .../components/Filter/datePickerValueUtils.ts | 0 .../components/Filter/havingFilterUtils.ts | 0 .../src/components/Filter/index.ts | 0 .../src/components/Filter/whereFilterUtils.ts | 0 .../src/components/Render/VSeedRender.tsx | 0 .../src/components/Render/index.ts | 0 .../src/components/Shelfs/DimensionShelf.tsx | 0 .../src/components/Shelfs/HavingShelf.tsx | 0 .../src/components/Shelfs/MeasureShelf.tsx | 0 .../src/components/Shelfs/WhereShelf.tsx | 0 .../components/Shelfs/common/FieldShelf.tsx | 0 .../components/Shelfs/common/FilterShelf.tsx | 0 .../Shelfs/common/MeasureFormatPanel.tsx | 0 .../Shelfs/common/ShelfRootOperatorButton.tsx | 0 .../components/Shelfs/common/ShelfTrack.tsx | 0 .../src/components/Shelfs/common/index.ts | 0 .../Shelfs/common/openShelfRenameModal.tsx | 0 .../Shelfs/dimensionDateAggregateUtils.ts | 0 .../src/components/Shelfs/dnd/DropArea.tsx | 0 .../Shelfs/dnd/ShelfBoundaryDropZone.tsx | 0 .../Shelfs/dnd/ShelfDndProvider.tsx | 0 .../Shelfs/dnd/ShelfDropIndicator.tsx | 0 .../Shelfs/dnd/ShelfItemDropZones.tsx | 0 .../src/components/Shelfs/dnd/dropLogic.ts | 0 .../src/components/Shelfs/dnd/index.ts | 0 .../src/components/Shelfs/dnd/types.ts | 0 .../Shelfs/dnd/useShelfDndRegistration.ts | 0 .../Shelfs/dnd/useShelfItemDropTargets.ts | 0 .../src/components/Shelfs/dragDropUtils.ts | 0 .../src/components/Shelfs/hooks/index.ts | 0 .../Shelfs/hooks/useFilterRootOperator.ts | 0 .../src/components/Shelfs/index.ts | 0 .../Shelfs/measureAggregateUtils.ts | 0 .../src/components/Shelfs/reorderUtils.ts | 0 .../src/components/Shelfs/shelfNameUtils.ts | 0 .../Shelfs/shelves/DimensionShelf.tsx | 0 .../components/Shelfs/shelves/HavingShelf.tsx | 0 .../Shelfs/shelves/MeasureShelf.tsx | 0 .../components/Shelfs/shelves/WhereShelf.tsx | 0 .../src/components/Shelfs/shelves/index.ts | 0 .../utils/dimensionDateAggregateUtils.ts | 0 .../components/Shelfs/utils/dragDropUtils.ts | 0 .../src/components/Shelfs/utils/index.ts | 0 .../Shelfs/utils/measureAggregateUtils.ts | 0 .../Shelfs/utils/measureFormatUtils.ts | 0 .../components/Shelfs/utils/menuItemUtils.tsx | 0 .../Shelfs/utils/menuSelectionUtils.ts | 0 .../components/Shelfs/utils/reorderUtils.ts | 0 .../components/Shelfs/utils/shelfNameUtils.ts | 0 .../src/components/Shelfs/utils/sortUtils.ts | 0 .../src/components/Toolbar/config.tsx | 0 .../src/components/Toolbar/index.tsx | 0 .../src/constants/builder.ts | 0 .../{demo => standard}/src/hooks/index.ts | 0 .../src/hooks/useBuilderDocState.ts | 0 .../src/hooks/useVBIBuilder.ts | 0 .../src/hooks/useVBIChartType.ts | 0 .../src/hooks/useVBIDimensions.ts | 0 .../src/hooks/useVBIHavingFilter.ts | 0 .../src/hooks/useVBIMeasures.ts | 0 .../src/hooks/useVBISchemaFields.ts | 0 .../src/hooks/useVBIStore.ts | 0 .../src/hooks/useVBIUndoManager.ts | 0 .../src/hooks/useVBIWhereFilter.ts | 0 .../{demo => standard}/src/i18n/index.ts | 0 .../src/i18n/locales/en-US.json | 0 .../src/i18n/locales/zh-CN.json | 0 .../src/i18n/useTranslation.ts | 0 .../{demo => standard}/src/i18n/utils.ts | 0 practices/{demo => standard}/src/index.tsx | 0 .../{demo => standard}/src/model/VBIStore.ts | 0 .../{demo => standard}/src/model/index.ts | 0 .../src/utils/demoConnector.ts | 0 .../{demo => standard}/src/utils/fieldRole.ts | 0 .../tests/datePickerValueUtils.test.ts | 0 .../tests/dimensionDateAggregateUtils.test.ts | 0 .../tests/dragDropUtils.test.ts | 0 .../tests/havingFilterUtils.test.ts | 0 .../{demo => standard}/tests/index.test.tsx | 0 .../tests/measureAggregateUtils.test.ts | 0 .../tests/reorderUtils.test.ts | 0 .../tests/shelfDndDropLogic.test.ts | 0 .../tests/shelfMenus.test.tsx | 0 practices/standard/tests/test.d.ts | 1 + .../{demo => standard}/tests/tsconfig.json | 0 .../tests/whereFilterUtils.test.ts | 0 practices/{demo => standard}/tsconfig.json | 0 practices/standard/tsconfig.test.json | 13 ++ 187 files changed, 2053 insertions(+), 153 deletions(-) create mode 100644 apps/website/docs/zh-CN/vbi/practices/standard-report.mdx rename packages/vbi/docs/{todo3-create-report => 2026-03-24-create-report}/adr.md (100%) rename packages/vbi/docs/{todo3-create-report => 2026-03-24-create-report}/goal.md (100%) rename packages/vbi/docs/{todo3-create-report => 2026-03-24-create-report}/plan.md (100%) create mode 100644 practices/standard-report/.gitignore rename practices/{demo => standard-report}/.prettierignore (100%) rename practices/{demo => standard-report}/.prettierrc (100%) create mode 100644 practices/standard-report/AGENTS.md create mode 100644 practices/standard-report/README.md create mode 100644 practices/standard-report/docs/2026-03-24-standard-report/adr.md rename practices/{demo/docs/todo2-standard-report => standard-report/docs/2026-03-24-standard-report}/goal.md (88%) create mode 100644 practices/standard-report/docs/2026-03-24-standard-report/plan.md create mode 100644 practices/standard-report/docs/README.md rename practices/{demo => standard-report}/eslint.config.mjs (100%) create mode 100644 practices/standard-report/package.json rename practices/{demo => standard-report}/rslib.config.ts (100%) rename practices/{demo => standard-report}/rstest.config.ts (100%) rename practices/{demo => standard-report}/rstest.setup.ts (100%) create mode 100644 practices/standard-report/src/App/App.tsx create mode 100644 practices/standard-report/src/App/components/editor/ReportEditorDrawer.tsx create mode 100644 practices/standard-report/src/App/components/page/PageHoverActions.tsx create mode 100644 practices/standard-report/src/App/components/page/PageInsight.tsx create mode 100644 practices/standard-report/src/App/components/page/PagePreviewCanvas.tsx create mode 100644 practices/standard-report/src/App/components/page/PagePreviewCard.tsx create mode 100644 practices/standard-report/src/App/components/stage/ReportCarousel.tsx create mode 100644 practices/standard-report/src/App/components/stage/StageNavButton.tsx create mode 100644 practices/standard-report/src/App/hooks/useCarouselDisplayIndex.ts create mode 100644 practices/standard-report/src/App/hooks/usePagePreview.ts rename practices/{demo => standard-report}/src/App/index.ts (100%) create mode 100644 practices/standard-report/src/App/layout/ReportStage.tsx create mode 100644 practices/standard-report/src/App/layout/ReportWorkbench.tsx create mode 100644 practices/standard-report/src/App/layout/view-config.ts create mode 100644 practices/standard-report/src/App/styles/editor.css create mode 100644 practices/standard-report/src/App/styles/index.css create mode 100644 practices/standard-report/src/App/styles/insight.css create mode 100644 practices/standard-report/src/App/styles/layout.css create mode 100644 practices/standard-report/src/App/styles/page-actions.css create mode 100644 practices/standard-report/src/App/styles/page.css create mode 100644 practices/standard-report/src/App/styles/placeholder.css create mode 100644 practices/standard-report/src/App/styles/stage.css create mode 100644 practices/standard-report/src/constants/builder.ts create mode 100644 practices/standard-report/src/index.tsx create mode 100644 practices/standard-report/src/model/index.ts create mode 100644 practices/standard-report/src/model/report-store.ts create mode 100644 practices/standard-report/src/utils/demoConnector.ts create mode 100644 practices/standard-report/src/utils/report-pages.ts create mode 100644 practices/standard-report/tests/page-hover-actions.test.tsx create mode 100644 practices/standard-report/tests/report-pages.test.ts rename practices/{demo => standard-report}/tests/test.d.ts (100%) create mode 100644 practices/standard-report/tsconfig.json rename practices/{demo => standard-report}/tsconfig.test.json (100%) rename practices/{demo => standard}/.gitignore (100%) create mode 100644 practices/standard/.prettierignore create mode 100644 practices/standard/.prettierrc rename practices/{demo => standard}/AGENTS.md (100%) rename practices/{demo => standard}/README.md (100%) rename practices/{demo => standard}/docs/2026-03-20-shelves/adr.md (79%) rename practices/{demo => standard}/docs/2026-03-20-shelves/goal.md (90%) rename practices/{demo => standard}/docs/README.md (63%) rename practices/{demo => standard}/docs/todo1-upload-csv/goal.md (86%) create mode 100644 practices/standard/eslint.config.mjs rename practices/{demo => standard}/package.json (98%) create mode 100644 practices/standard/rslib.config.ts create mode 100644 practices/standard/rstest.config.ts create mode 100644 practices/standard/rstest.setup.ts rename practices/{demo => standard}/src/App/App.tsx (100%) rename practices/{demo => standard}/src/App/app.css (100%) rename practices/{demo => standard}/src/App/components/ChartPanel.tsx (100%) rename practices/{demo => standard}/src/App/components/FieldsPanel.tsx (100%) rename practices/{demo => standard}/src/App/components/FilterRootOperatorToggle.tsx (100%) rename practices/{demo => standard}/src/App/components/ShelfPanel.tsx (100%) rename practices/{demo => standard}/src/App/components/ShelfRow.tsx (100%) rename practices/{demo => standard}/src/App/components/index.ts (100%) create mode 100644 practices/standard/src/App/index.ts rename practices/{demo => standard}/src/components/ChartType/Selector.tsx (100%) rename practices/{demo => standard}/src/components/ChartType/index.ts (100%) rename practices/{demo => standard}/src/components/Fields/DimensionsList/index.tsx (100%) rename practices/{demo => standard}/src/components/Fields/FieldList/index.tsx (100%) rename practices/{demo => standard}/src/components/Fields/MeasuresList/index.tsx (100%) rename practices/{demo => standard}/src/components/Filter/DateFilterEditor.tsx (100%) rename practices/{demo => standard}/src/components/Filter/FilterPanel.tsx (100%) rename practices/{demo => standard}/src/components/Filter/HavingFilterPanel.tsx (100%) rename practices/{demo => standard}/src/components/Filter/dateFilterUtils.ts (100%) rename practices/{demo => standard}/src/components/Filter/datePickerValueUtils.ts (100%) rename practices/{demo => standard}/src/components/Filter/havingFilterUtils.ts (100%) rename practices/{demo => standard}/src/components/Filter/index.ts (100%) rename practices/{demo => standard}/src/components/Filter/whereFilterUtils.ts (100%) rename practices/{demo => standard}/src/components/Render/VSeedRender.tsx (100%) rename practices/{demo => standard}/src/components/Render/index.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/DimensionShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/HavingShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/MeasureShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/WhereShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/common/FieldShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/common/FilterShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/common/MeasureFormatPanel.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/common/ShelfRootOperatorButton.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/common/ShelfTrack.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/common/index.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/common/openShelfRenameModal.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/dimensionDateAggregateUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/DropArea.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/ShelfBoundaryDropZone.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/ShelfDndProvider.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/ShelfDropIndicator.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/ShelfItemDropZones.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/dropLogic.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/index.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/types.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/useShelfDndRegistration.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/dnd/useShelfItemDropTargets.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/dragDropUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/hooks/index.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/hooks/useFilterRootOperator.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/index.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/measureAggregateUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/reorderUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/shelfNameUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/shelves/DimensionShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/shelves/HavingShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/shelves/MeasureShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/shelves/WhereShelf.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/shelves/index.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/dimensionDateAggregateUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/dragDropUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/index.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/measureAggregateUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/measureFormatUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/menuItemUtils.tsx (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/menuSelectionUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/reorderUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/shelfNameUtils.ts (100%) rename practices/{demo => standard}/src/components/Shelfs/utils/sortUtils.ts (100%) rename practices/{demo => standard}/src/components/Toolbar/config.tsx (100%) rename practices/{demo => standard}/src/components/Toolbar/index.tsx (100%) rename practices/{demo => standard}/src/constants/builder.ts (100%) rename practices/{demo => standard}/src/hooks/index.ts (100%) rename practices/{demo => standard}/src/hooks/useBuilderDocState.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIBuilder.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIChartType.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIDimensions.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIHavingFilter.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIMeasures.ts (100%) rename practices/{demo => standard}/src/hooks/useVBISchemaFields.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIStore.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIUndoManager.ts (100%) rename practices/{demo => standard}/src/hooks/useVBIWhereFilter.ts (100%) rename practices/{demo => standard}/src/i18n/index.ts (100%) rename practices/{demo => standard}/src/i18n/locales/en-US.json (100%) rename practices/{demo => standard}/src/i18n/locales/zh-CN.json (100%) rename practices/{demo => standard}/src/i18n/useTranslation.ts (100%) rename practices/{demo => standard}/src/i18n/utils.ts (100%) rename practices/{demo => standard}/src/index.tsx (100%) rename practices/{demo => standard}/src/model/VBIStore.ts (100%) rename practices/{demo => standard}/src/model/index.ts (100%) rename practices/{demo => standard}/src/utils/demoConnector.ts (100%) rename practices/{demo => standard}/src/utils/fieldRole.ts (100%) rename practices/{demo => standard}/tests/datePickerValueUtils.test.ts (100%) rename practices/{demo => standard}/tests/dimensionDateAggregateUtils.test.ts (100%) rename practices/{demo => standard}/tests/dragDropUtils.test.ts (100%) rename practices/{demo => standard}/tests/havingFilterUtils.test.ts (100%) rename practices/{demo => standard}/tests/index.test.tsx (100%) rename practices/{demo => standard}/tests/measureAggregateUtils.test.ts (100%) rename practices/{demo => standard}/tests/reorderUtils.test.ts (100%) rename practices/{demo => standard}/tests/shelfDndDropLogic.test.ts (100%) rename practices/{demo => standard}/tests/shelfMenus.test.tsx (100%) create mode 100644 practices/standard/tests/test.d.ts rename practices/{demo => standard}/tests/tsconfig.json (100%) rename practices/{demo => standard}/tests/whereFilterUtils.test.ts (100%) rename practices/{demo => standard}/tsconfig.json (100%) create mode 100644 practices/standard/tsconfig.test.json diff --git a/AGENTS.md b/AGENTS.md index 65cc548553..763c7787f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ VBI/ │ ├── src/ │ └── docs/ ├── practices/ # 不同复杂度的实践示例 -│ ├── demo/ # 标准版示例 +│ ├── standard/ # 标准版示例 │ │ ├── src/ │ │ └── docs/ │ ├── minimalist/ # 极简实现示例 diff --git a/CLAUDE.md b/CLAUDE.md index dc42ebbeca..475ab5beae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,7 +44,7 @@ VBI/ │ ├── src/ │ └── docs/ ├── practices/ # 不同复杂度的实践示例 -│ ├── demo/ # 标准版示例 +│ ├── standard/ # 标准版示例 │ │ ├── src/ │ │ └── docs/ │ ├── minimalist/ # 极简实现示例 diff --git a/apps/vbi_fe/package.json b/apps/vbi_fe/package.json index d43a9d9c2d..36fec07e5c 100644 --- a/apps/vbi_fe/package.json +++ b/apps/vbi_fe/package.json @@ -20,7 +20,7 @@ "@visactor/vquery": "workspace:*", "@visactor/vseed": "workspace:*", "antd": "6.1.3", - "demo": "workspace:*", + "standard": "workspace:*", "react": "19.2.3", "react-dom": "19.2.3", "react-router-dom": "7.12.0", diff --git a/apps/vbi_fe/src/pages/DocumentEditorPage.tsx b/apps/vbi_fe/src/pages/DocumentEditorPage.tsx index a3df608c24..41758659f9 100644 --- a/apps/vbi_fe/src/pages/DocumentEditorPage.tsx +++ b/apps/vbi_fe/src/pages/DocumentEditorPage.tsx @@ -2,7 +2,7 @@ import { memo } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Button, Layout, Spin, Typography, Space } from 'antd'; import { ArrowLeftOutlined } from '@ant-design/icons'; -import { APP } from 'demo'; +import { APP } from 'standard'; import { useCollaborativeBuilder } from '../hooks/useCollaborativeBuilder'; import { Collaborators } from '../components/Collaborators'; diff --git a/apps/vbi_fe/tsconfig.json b/apps/vbi_fe/tsconfig.json index 849a865ce2..d2df894d18 100644 --- a/apps/vbi_fe/tsconfig.json +++ b/apps/vbi_fe/tsconfig.json @@ -38,7 +38,7 @@ "path": "../../packages/vbi" }, { - "path": "../../practices/demo" + "path": "../../practices/standard" } ], "include": ["src"] diff --git a/apps/website/docs/zh-CN/vbi/practices/_meta.json b/apps/website/docs/zh-CN/vbi/practices/_meta.json index 0d866e2108..687a04756b 100644 --- a/apps/website/docs/zh-CN/vbi/practices/_meta.json +++ b/apps/website/docs/zh-CN/vbi/practices/_meta.json @@ -1,5 +1,6 @@ [ { "type": "file", "name": "standard", "label": "标准" }, + { "type": "file", "name": "standard-report", "label": "标准报表" }, { "type": "file", "name": "minimalist", "label": "极简" }, { "type": "file", "name": "streamlined", "label": "精简" }, { "type": "file", "name": "professional", "label": "专业" }, diff --git a/apps/website/docs/zh-CN/vbi/practices/standard-report.mdx b/apps/website/docs/zh-CN/vbi/practices/standard-report.mdx new file mode 100644 index 0000000000..65b7d9d549 --- /dev/null +++ b/apps/website/docs/zh-CN/vbi/practices/standard-report.mdx @@ -0,0 +1,7 @@ +# Standard Report + +import { APP } from 'standard-report' + +
+ +
diff --git a/apps/website/docs/zh-CN/vbi/practices/standard.mdx b/apps/website/docs/zh-CN/vbi/practices/standard.mdx index 9bfdd97556..f43f8c50cc 100644 --- a/apps/website/docs/zh-CN/vbi/practices/standard.mdx +++ b/apps/website/docs/zh-CN/vbi/practices/standard.mdx @@ -1,6 +1,6 @@ # Standard -import { APP } from 'demo' +import { APP } from 'standard'
diff --git a/apps/website/package.json b/apps/website/package.json index c85c1a1dd2..7de62a8da8 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -18,7 +18,8 @@ "@visactor/vquery": "workspace:*", "@visactor/vseed": "workspace:*", "@visactor/vtable": "1.23.1", - "demo": "workspace:*", + "standard": "workspace:*", + "standard-report": "workspace:*", "minimalist": "workspace:*", "professional": "workspace:*", "streamlined": "workspace:*", diff --git a/apps/website/tsconfig.json b/apps/website/tsconfig.json index c4c94afd0b..0f13d3c378 100644 --- a/apps/website/tsconfig.json +++ b/apps/website/tsconfig.json @@ -29,7 +29,10 @@ "path": "../../packages/vbi" }, { - "path": "../../practices/demo" + "path": "../../practices/standard" + }, + { + "path": "../../practices/standard-report" }, { "path": "../../practices/minimalist" diff --git a/packages/vbi-react/DESIGN.md b/packages/vbi-react/DESIGN.md index fb2430c6f3..bee016228b 100644 --- a/packages/vbi-react/DESIGN.md +++ b/packages/vbi-react/DESIGN.md @@ -122,9 +122,9 @@ public buildVSeed = async (options?: { signal?: AbortSignal }): Promise { ## 11. Migration Strategy -### 11.1 From practices/demo +### 11.1 From practices/standard **Existing Code**: ```typescript -// practices/demo/src/hooks/useVBI.ts +// practices/standard/src/hooks/useVBI.ts import { useState, useEffect } from 'react' import { VBIChartBuilder } from '@visactor/vbi' @@ -1142,7 +1142,7 @@ const { dsl, builder } = useVBI(builder) 3. **Phase 3: Extract a standalone validation demo** - Extract the current starter preview into `practices/vbi-react-starter` - Restore `practices/professional` to its original role instead of keeping it as the long-term `vbi-react` showcase - - Keep `practices/demo`, `practices/minimalist`, and `practices/streamlined` unchanged during this rollout + - Keep `practices/standard`, `practices/minimalist`, and `practices/streamlined` unchanged during this rollout 4. **Phase 4: Documentation and release** - Reuse the completed components as website examples @@ -1158,7 +1158,7 @@ Rules for that stage: - `practices/vbi-react-starter` is the designated demo for validating the components layer end to end - `practices/professional` may still be used later for regression spot-checks, but it should not remain the primary `vbi-react` showcase - Current rollout status: `practices/vbi-react-starter` now exists as the standalone validation demo, and `practices/professional` is no longer the long-term host for the component showcase -- Do **not** modify `practices/demo`, `practices/minimalist`, or `practices/streamlined` as part of the components rollout unless a later explicit decision changes this scope +- Do **not** modify `practices/standard`, `practices/minimalist`, or `practices/streamlined` as part of the components rollout unless a later explicit decision changes this scope - Restoring `practices/professional` back toward its original pre-`vbi-react` role is allowed during this extraction - This scope supersedes the earlier plan that used `practices/professional` as the permanent first-line integration target @@ -1179,7 +1179,7 @@ This design will be integrated into the VBI documentation website under the **VB | **Hooks API** | Detailed hook documentation with examples | | **Components** | Prebuilt component reference | | **Examples** | Common usage patterns | -| **Migration Guide** | Upgrading from practices/demo | +| **Migration Guide** | Upgrading from practices/standard | ### 12.3 Design Document Integration @@ -1241,15 +1241,15 @@ This design document will become part of the architecture documentation, providi ### Phase 4: Integration and Documentation (1 week) -| Week | Task | Deliverable | Status | -| ---- | ------------------------------------------------------------------------------------------------------------------------ | -------------------- | --------------- | -| 7 | Restore `practices/professional` to its original role | legacy professional | **Completed** | -| 7 | Extract `practices/vbi-react-starter` for components validation | standalone demo | **Completed** | -| 7 | Keep `practices/demo`, `practices/minimalist`, and `practices/streamlined` unchanged during the first components rollout | scope guard | **In Progress** | -| 7 | Write README documentation | Documentation | **Pending** | -| 7 | Integrate docs under VBI website section | documentation pages | **In Progress** | -| 7 | Keep `DESIGN.md` synchronized with implementation status | living design record | **In Progress** | -| 7 | Publish Beta version | npm package | **Pending** | +| Week | Task | Deliverable | Status | +| ---- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------- | --------------- | +| 7 | Restore `practices/professional` to its original role | legacy professional | **Completed** | +| 7 | Extract `practices/vbi-react-starter` for components validation | standalone demo | **Completed** | +| 7 | Keep `practices/standard`, `practices/minimalist`, and `practices/streamlined` unchanged during the first components rollout | scope guard | **In Progress** | +| 7 | Write README documentation | Documentation | **Pending** | +| 7 | Integrate docs under VBI website section | documentation pages | **In Progress** | +| 7 | Keep `DESIGN.md` synchronized with implementation status | living design record | **In Progress** | +| 7 | Publish Beta version | npm package | **Pending** | --- @@ -1266,7 +1266,7 @@ This design document will become part of the architecture documentation, providi | **Testing** | Hook unit tests and starter component interaction tests are active today; richer snapshot coverage can be added later | | **Key Challenges** | Yjs lifecycle management, useSyncExternalStore adaptation | | **Estimated Timeline** | 7 weeks for MVP | -| **Backward Compatibility** | Smooth migration from practices/demo | +| **Backward Compatibility** | Smooth migration from practices/standard | | **Documentation Discipline** | DESIGN.md tracks completed vs pending work continuously | --- diff --git a/packages/vbi/docs/2026-03-19-measure-format/adr.md b/packages/vbi/docs/2026-03-19-measure-format/adr.md index 92e4d271ba..bbdbe545b2 100644 --- a/packages/vbi/docs/2026-03-19-measure-format/adr.md +++ b/packages/vbi/docs/2026-03-19-measure-format/adr.md @@ -11,7 +11,7 @@ VSeed 的 `Measure` 已支持数值格式相关配置: - `autoFormat?: boolean` - `numFormat?: NumFormat` -当前 VBI 还没有把这组能力完整接通到自己的 DSL、builder 和 `buildVSeed` 适配层;`practices/demo` 的 measure shelf 也还不能在 UI 上编辑格式配置。 +当前 VBI 还没有把这组能力完整接通到自己的 DSL、builder 和 `buildVSeed` 适配层;`practices/standard` 的 measure shelf 也还不能在 UI 上编辑格式配置。 本 ADR 的目标是将 measure format 能力以单一接口接入 VBI,并补齐到 Demo UI。 @@ -108,13 +108,13 @@ VBI 不向 VSeed 透传 `format` 字段本身。 VBI 的职责是表达统一配置,并在适配阶段映射为 VSeed 所需字段;VBI 不实现 formatter 创建逻辑,也不复制 VSeed 的格式化规则。 -### 5. `practices/demo` 需要补齐格式设置 UI +### 5. `practices/standard` 需要补齐格式设置 UI Demo 需要把这组能力暴露到 measure shelf 的交互中。 接入方式: -1. 在 `practices/demo/src/components/Shelfs/shelves/MeasureShelf.tsx` 的 measure 菜单中新增 `Format` 入口 +1. 在 `practices/standard/src/components/Shelfs/shelves/MeasureShelf.tsx` 的 measure 菜单中新增 `Format` 入口 2. `Format` 不做多级 submenu,改为打开独立弹窗 3. 弹窗沿用当前 shelf 交互模式,建议新增一个与 `openShelfRenameModal` 同级的 `openMeasureFormatModal` @@ -134,8 +134,8 @@ UI 结构: 对应配套改动: -1. `practices/demo/src/hooks/useVBIMeasures.ts` 中的 `MeasureNodeLike` 需要补上 `setFormat` / `getFormat` / `clearFormat` -2. `practices/demo/src/i18n/locales/zh-CN.json` 和 `practices/demo/src/i18n/locales/en-US.json` 需要新增 format 相关文案 +1. `practices/standard/src/hooks/useVBIMeasures.ts` 中的 `MeasureNodeLike` 需要补上 `setFormat` / `getFormat` / `clearFormat` +2. `practices/standard/src/i18n/locales/zh-CN.json` 和 `practices/standard/src/i18n/locales/en-US.json` 需要新增 format 相关文案 3. 如需在列表中提示当前状态,可在 measure 标签上补一个轻量摘要,例如“自动”或“自定义”,但这不是首批必需项 ### 6. 测试范围 @@ -174,8 +174,8 @@ Demo 测试覆盖以下内容: - VBI `MeasureNodeBuilder`: `packages/vbi/src/builder/features/measures/mea-node-builder.ts` - VBI `MeasuresBuilder`: `packages/vbi/src/builder/features/measures/mea-builder.ts` - VBI `buildVSeed`: `packages/vbi/src/builder/adapters/vquery-vseed/build-vseed.ts` -- Demo measure shelf: `practices/demo/src/components/Shelfs/shelves/MeasureShelf.tsx` -- Demo measures hook: `practices/demo/src/hooks/useVBIMeasures.ts` +- Demo measure shelf: `practices/standard/src/components/Shelfs/shelves/MeasureShelf.tsx` +- Demo measures hook: `practices/standard/src/hooks/useVBIMeasures.ts` ## 淘汰内容概述 diff --git a/packages/vbi/docs/2026-03-19-measure-format/plan.md b/packages/vbi/docs/2026-03-19-measure-format/plan.md index 8f0b7b4e8a..0778acad5b 100644 --- a/packages/vbi/docs/2026-03-19-measure-format/plan.md +++ b/packages/vbi/docs/2026-03-19-measure-format/plan.md @@ -5,7 +5,7 @@ ## 范围 -本计划聚焦 VBI 核心包(`packages/vbi`),不含 `practices/demo` UI 改造。 +本计划聚焦 VBI 核心包(`packages/vbi`),不含 `practices/standard` UI 改造。 ## Phase 1: 类型定义 diff --git a/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/adr.md b/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/adr.md index 28436d8ec8..0720add508 100644 --- a/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/adr.md +++ b/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/adr.md @@ -12,10 +12,10 @@ Proposed 2. 无法稳定表达“2024 年”“2024-Q1”“2024-03”这类自然周期筛选。 3. 旧草案中的 `dateOp`、`dateValue`、`granularity` 是平铺字段,容易产生非法组合,也会把日期语义拆散。 -同时,`practices/demo` 当前的 `where` UI 仍然是纯标量表单: +同时,`practices/standard` 当前的 `where` UI 仍然是纯标量表单: -1. [`FilterPanel.tsx`](../../practices/demo/src/components/Filter/FilterPanel.tsx) 只支持通用比较表单、`range`、`tags` 这类输入策略。 -2. [`WhereShelf.tsx`](../../practices/demo/src/components/Shelfs/shelves/WhereShelf.tsx) 和 [`useVBIWhereFilter.ts`](../../practices/demo/src/hooks/useVBIWhereFilter.ts) 只知道 `setOperator(...)` / `setValue(...)`。 +1. [`FilterPanel.tsx`](../../practices/standard/src/components/Filter/FilterPanel.tsx) 只支持通用比较表单、`range`、`tags` 这类输入策略。 +2. [`WhereShelf.tsx`](../../practices/standard/src/components/Shelfs/shelves/WhereShelf.tsx) 和 [`useVBIWhereFilter.ts`](../../practices/standard/src/hooks/useVBIWhereFilter.ts) 只知道 `setOperator(...)` / `setValue(...)`。 3. 日期维度虽然已有 `isDate` 标记,但还没有独立的日期筛选编辑器。 底层 `vquery` 已经补齐了当前阶段需要依赖的基础验证: @@ -296,7 +296,7 @@ const zVBIWhereScalarFilter = z.object({ export const zVBIWhereFilter = z.union([zVBIWhereDateFilter, zVBIWhereScalarFilter]) ``` -### 8. `practices/demo` UI 采用“同一入口、日期分流”的编辑方案 +### 8. `practices/standard` UI 采用“同一入口、日期分流”的编辑方案 UI 目标不是在现有标量表单里硬塞更多字段,而是在同一个 `where` 编辑入口里,对日期字段切换到独立的日期编辑模式。 @@ -309,7 +309,7 @@ UI 目标不是在现有标量表单里硬塞更多字段,而是在同一个 ` #### 建议的表单模型 -`practices/demo` 内部可以继续复用现有 `FilterItem` 外形,但在类型上要把日期分支单独表达出来: +`practices/standard` 内部可以继续复用现有 `FilterItem` 外形,但在类型上要把日期分支单独表达出来: ```typescript type DemoWhereScalarFilterItem = { @@ -353,21 +353,21 @@ type DemoWhereFilterItem = DemoWhereScalarFilterItem | DemoWhereDateFilterItem #### 组件改造点 -- [`practices/demo/src/components/Filter/FilterPanel.tsx`](../../practices/demo/src/components/Filter/FilterPanel.tsx) +- [`practices/standard/src/components/Filter/FilterPanel.tsx`](../../practices/standard/src/components/Filter/FilterPanel.tsx) - 根据 `field.isDate` 分流为标量编辑器或日期编辑器。 - 日期编辑器使用 `type` 选择器驱动动态子表单。 - 提交时,如果是日期模式,输出 `{ op: 'date', value: DatePredicate }`。 -- [`practices/demo/src/components/Filter/whereFilterUtils.ts`](../../practices/demo/src/components/Filter/whereFilterUtils.ts) +- [`practices/standard/src/components/Filter/whereFilterUtils.ts`](../../practices/standard/src/components/Filter/whereFilterUtils.ts) - 保留现有标量 operator/input strategy 工具函数。 - 新增日期表单的默认值、序列化、反序列化、展示文案生成工具。 - `getWhereDisplayText(...)` 需要支持 `op === 'date'` 的可读展示。 -- [`practices/demo/src/components/Shelfs/shelves/WhereShelf.tsx`](../../practices/demo/src/components/Shelfs/shelves/WhereShelf.tsx) +- [`practices/standard/src/components/Shelfs/shelves/WhereShelf.tsx`](../../practices/standard/src/components/Shelfs/shelves/WhereShelf.tsx) - 新增 `item.op === 'date'` 分支。 - add/update 时,日期节点改用 `node.setDate(...)`,非日期节点继续用 `setOperator(...)` / `setValue(...)`。 -- [`practices/demo/src/hooks/useVBIWhereFilter.ts`](../../practices/demo/src/hooks/useVBIWhereFilter.ts) +- [`practices/standard/src/hooks/useVBIWhereFilter.ts`](../../practices/standard/src/hooks/useVBIWhereFilter.ts) - mutator typing 增加 `setDate(...)`。 - `VBIWhereFilter` 命名同步替代旧的 `VBIFilter`。 @@ -383,7 +383,7 @@ type DemoWhereFilterItem = DemoWhereScalarFilterItem | DemoWhereDateFilterItem #### UI 测试要求 -`practices/demo` 至少需要补: +`practices/standard` 至少需要补: 1. 日期 filter 从 builder 值回填到表单的测试。 2. 日期表单序列化为 `{ op: 'date', value: DatePredicate }` 的测试。 @@ -412,7 +412,7 @@ type DemoWhereFilterItem = DemoWhereScalarFilterItem | DemoWhereDateFilterItem #### Demo 必补测试 -在 `practices/demo` UI 合入前,必须新增并通过: +在 `practices/standard` UI 合入前,必须新增并通过: 1. `whereFilterUtils` 的日期序列化与展示文案测试。 2. `FilterPanel` 的日期编辑模式测试。 @@ -425,13 +425,13 @@ type DemoWhereFilterItem = DemoWhereScalarFilterItem | DemoWhereDateFilterItem 1. `whereFilter` 的类型命名与模块风格统一。 2. 日期筛选 DSL 收敛为一个稳定入口,不再有平铺字段拼装。 3. builder API 保持克制,复杂度集中在 `DatePredicate`。 -4. `practices/demo` 能在不复制 DSL 的前提下,支持四类日期筛选器。 +4. `practices/standard` 能在不复制 DSL 的前提下,支持四类日期筛选器。 5. lowering 目标完全依赖已验证的 `vquery` 基础能力。 ### Negative 1. VBI 需要承担日期语义解析和时间上下文管理职责。 -2. `practices/demo` 的表单状态会从单一路径变成标量模式和日期模式两条路径。 +2. `practices/standard` 的表单状态会从单一路径变成标量模式和日期模式两条路径。 3. 日期边界行为如果没有测试锁定,风险会直接体现在 query 结果上。 ## Implementation Impact @@ -442,23 +442,23 @@ type DemoWhereFilterItem = DemoWhereScalarFilterItem | DemoWhereDateFilterItem - `packages/vbi/src/builder/features/whereFilter/where-node-builder.ts` - `packages/vbi/src/pipeline/vqueryDSL/buildWhere.ts` - `packages/vbi/tests/builder/features/whereFilter.test.ts` -- `practices/demo/src/components/Filter/FilterPanel.tsx` -- `practices/demo/src/components/Filter/whereFilterUtils.ts` -- `practices/demo/src/components/Shelfs/shelves/WhereShelf.tsx` -- `practices/demo/src/hooks/useVBIWhereFilter.ts` -- `practices/demo/src/i18n/locales/zh-CN.json` -- `practices/demo/src/i18n/locales/en-US.json` -- `practices/demo/tests/whereFilterUtils.test.ts` +- `practices/standard/src/components/Filter/FilterPanel.tsx` +- `practices/standard/src/components/Filter/whereFilterUtils.ts` +- `practices/standard/src/components/Shelfs/shelves/WhereShelf.tsx` +- `practices/standard/src/hooks/useVBIWhereFilter.ts` +- `practices/standard/src/i18n/locales/zh-CN.json` +- `practices/standard/src/i18n/locales/en-US.json` +- `practices/standard/tests/whereFilterUtils.test.ts` ## Reference - VBI WhereFilter Types: `packages/vbi/src/types/dsl/whereFilter/filters.ts` - VBI WhereFilter Node Builder: `packages/vbi/src/builder/features/whereFilter/where-node-builder.ts` - VBI buildWhere: `packages/vbi/src/pipeline/vqueryDSL/buildWhere.ts` -- Demo Filter Panel: `practices/demo/src/components/Filter/FilterPanel.tsx` -- Demo Where Utils: `practices/demo/src/components/Filter/whereFilterUtils.ts` -- Demo Where Shelf: `practices/demo/src/components/Shelfs/shelves/WhereShelf.tsx` -- Demo Where Hook: `practices/demo/src/hooks/useVBIWhereFilter.ts` +- Demo Filter Panel: `practices/standard/src/components/Filter/FilterPanel.tsx` +- Demo Where Utils: `practices/standard/src/components/Filter/whereFilterUtils.ts` +- Demo Where Shelf: `practices/standard/src/components/Shelfs/shelves/WhereShelf.tsx` +- Demo Where Hook: `practices/standard/src/hooks/useVBIWhereFilter.ts` - VQuery Where Tests: `packages/vquery/tests/unit/sql-builder/builders/where.test.ts` ## 淘汰内容简述 diff --git a/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/plan.md b/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/plan.md index ff77b7b74d..6f213ba621 100644 --- a/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/plan.md +++ b/packages/vbi/docs/2026-03-19-vbi-date-dimension-filter/plan.md @@ -5,7 +5,7 @@ ## 范围 -本计划聚焦 VBI 核心包(`packages/vbi`),不含 `practices/demo` UI 改造。 +本计划聚焦 VBI 核心包(`packages/vbi`),不含 `practices/standard` UI 改造。 ## Phase 1: 类型定义与 Schema diff --git a/packages/vbi/docs/2026-03-23-mea-dim-sort/plan.md b/packages/vbi/docs/2026-03-23-mea-dim-sort/plan.md index 95ea21c036..0888c36272 100644 --- a/packages/vbi/docs/2026-03-23-mea-dim-sort/plan.md +++ b/packages/vbi/docs/2026-03-23-mea-dim-sort/plan.md @@ -5,7 +5,7 @@ ## 范围 -本计划聚焦 VBI 核心包(`packages/vbi`),只处理排序 DSL、builder、`buildVQuery().orderBy` lowering,以及由默认排序引起的生成物和快照更新;不包含 `practices/demo` UI 改造。 +本计划聚焦 VBI 核心包(`packages/vbi`),只处理排序 DSL、builder、`buildVQuery().orderBy` lowering,以及由默认排序引起的生成物和快照更新;不包含 `practices/standard` UI 改造。 ## Phase 1: 排序类型与 Schema diff --git a/packages/vbi/docs/todo3-create-report/adr.md b/packages/vbi/docs/2026-03-24-create-report/adr.md similarity index 100% rename from packages/vbi/docs/todo3-create-report/adr.md rename to packages/vbi/docs/2026-03-24-create-report/adr.md diff --git a/packages/vbi/docs/todo3-create-report/goal.md b/packages/vbi/docs/2026-03-24-create-report/goal.md similarity index 100% rename from packages/vbi/docs/todo3-create-report/goal.md rename to packages/vbi/docs/2026-03-24-create-report/goal.md diff --git a/packages/vbi/docs/todo3-create-report/plan.md b/packages/vbi/docs/2026-03-24-create-report/plan.md similarity index 100% rename from packages/vbi/docs/todo3-create-report/plan.md rename to packages/vbi/docs/2026-03-24-create-report/plan.md diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bc7e584f6..0c878ef3c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -204,9 +204,6 @@ importers: antd: specifier: 6.1.3 version: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - demo: - specifier: workspace:* - version: link:../../practices/demo react: specifier: 19.2.3 version: 19.2.3 @@ -219,6 +216,9 @@ importers: remeda: specifier: 2.28.0 version: 2.28.0 + standard: + specifier: workspace:* + version: link:../../practices/standard yjs: specifier: 13.6.28 version: 13.6.28 @@ -295,15 +295,18 @@ importers: '@visactor/vtable': specifier: 1.23.1 version: 1.23.1 - demo: - specifier: workspace:* - version: link:../../practices/demo minimalist: specifier: workspace:* version: link:../../practices/minimalist professional: specifier: workspace:* version: link:../../practices/professional + standard: + specifier: workspace:* + version: link:../../practices/standard + standard-report: + specifier: workspace:* + version: link:../../practices/standard-report streamlined: specifier: workspace:* version: link:../../practices/streamlined @@ -580,23 +583,11 @@ importers: specifier: 3.2.4 version: 3.2.4(@types/debug@4.1.13)(@types/node@24.10.1)(happy-dom@20.8.4)(jiti@2.6.1)(jsdom@26.1.0)(terser@5.46.1)(yaml@2.8.2) - practices/demo: + practices/minimalist: dependencies: '@ant-design/icons': specifier: 6.1.0 version: 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@dnd-kit/core': - specifier: ^6.3.1 - version: 6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@dnd-kit/modifiers': - specifier: ^9.0.0 - version: 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) - '@dnd-kit/sortable': - specifier: ^10.0.0 - version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) - '@dnd-kit/utilities': - specifier: ^3.2.2 - version: 3.2.2(react@19.2.3) '@visactor/vbi': specifier: workspace:* version: link:../../packages/vbi @@ -615,9 +606,85 @@ importers: antd: specifier: 6.1.3 version: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - dayjs: - specifier: 1.11.20 - version: 1.11.20 + react-dom: + specifier: '>=16.9.0' + version: 19.2.3(react@19.2.3) + zustand: + specifier: 5.0.6 + version: 5.0.6(@types/react@19.2.14)(react@19.2.3) + devDependencies: + '@eslint/js': + specifier: ^9.39.1 + version: 9.39.1 + '@rsbuild/plugin-react': + specifier: ^1.4.3 + version: 1.4.6(@rsbuild/core@1.7.3) + '@rslib/core': + specifier: ^0.19.3 + version: 0.19.6(typescript@5.9.3) + '@rstest/adapter-rslib': + specifier: ^0.1.1 + version: 0.1.1(@rslib/core@0.19.6(typescript@5.9.3))(typescript@5.9.3) + '@rstest/core': + specifier: 0.8.3 + version: 0.8.3(happy-dom@20.8.4)(jsdom@27.3.0) + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.1 + version: 16.3.1(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@types/react': + specifier: ^19.2.8 + version: 19.2.14 + eslint: + specifier: ^9.39.1 + version: 9.39.1(jiti@2.6.1) + globals: + specifier: ^16.5.0 + version: 16.5.0 + happy-dom: + specifier: ^20.3.3 + version: 20.8.4 + prettier: + specifier: ^3.7.3 + version: 3.7.3 + react: + specifier: ^19.2.3 + version: 19.2.3 + typescript: + specifier: 5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.48.0 + version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + + practices/professional: + dependencies: + '@ant-design/icons': + specifier: 6.1.0 + version: 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@visactor/vbi': + specifier: workspace:* + version: link:../../packages/vbi + '@visactor/vbi-react': + specifier: workspace:* + version: link:../../packages/vbi-react + '@visactor/vchart': + specifier: 2.0.15 + version: 2.0.15 + '@visactor/vquery': + specifier: workspace:* + version: link:../../packages/vquery + '@visactor/vseed': + specifier: workspace:* + version: link:../../packages/vseed + '@visactor/vtable': + specifier: 1.23.1 + version: 1.23.1 + antd: + specifier: 6.1.3 + version: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) zustand: specifier: 5.0.6 version: 5.0.6(@types/react@19.2.7)(react@19.2.3) @@ -643,6 +710,9 @@ importers: '@types/react': specifier: 19.2.7 version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) eslint: specifier: 9.39.1 version: 9.39.1(jiti@2.6.1) @@ -668,11 +738,23 @@ importers: specifier: 8.48.0 version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - practices/minimalist: + practices/standard: dependencies: '@ant-design/icons': specifier: 6.1.0 version: 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@dnd-kit/core': + specifier: ^6.3.1 + version: 6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@dnd-kit/modifiers': + specifier: ^9.0.0 + version: 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) + '@dnd-kit/sortable': + specifier: ^10.0.0 + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@19.2.3) '@visactor/vbi': specifier: workspace:* version: link:../../packages/vbi @@ -691,60 +773,60 @@ importers: antd: specifier: 6.1.3 version: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-dom: - specifier: '>=16.9.0' - version: 19.2.3(react@19.2.3) + dayjs: + specifier: 1.11.20 + version: 1.11.20 zustand: specifier: 5.0.6 - version: 5.0.6(@types/react@19.2.14)(react@19.2.3) + version: 5.0.6(@types/react@19.2.7)(react@19.2.3) devDependencies: '@eslint/js': - specifier: ^9.39.1 + specifier: 9.39.1 version: 9.39.1 '@rsbuild/plugin-react': - specifier: ^1.4.3 - version: 1.4.6(@rsbuild/core@1.7.3) + specifier: 1.4.2 + version: 1.4.2(@rsbuild/core@2.0.0-beta.6(core-js@3.47.0)) '@rslib/core': - specifier: ^0.19.3 - version: 0.19.6(typescript@5.9.3) - '@rstest/adapter-rslib': - specifier: ^0.1.1 - version: 0.1.1(@rslib/core@0.19.6(typescript@5.9.3))(typescript@5.9.3) + specifier: 0.18.6 + version: 0.18.6(typescript@5.9.3) '@rstest/core': specifier: 0.8.3 - version: 0.8.3(happy-dom@20.8.4)(jsdom@27.3.0) + version: 0.8.3(happy-dom@20.8.4)(jsdom@26.1.0) '@testing-library/jest-dom': - specifier: ^6.9.1 + specifier: 6.9.1 version: 6.9.1 '@testing-library/react': - specifier: ^16.3.1 - version: 16.3.1(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 16.3.1 + version: 16.3.1(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@types/react': - specifier: ^19.2.8 - version: 19.2.14 + specifier: 19.2.7 + version: 19.2.7 eslint: - specifier: ^9.39.1 + specifier: 9.39.1 version: 9.39.1(jiti@2.6.1) globals: - specifier: ^16.5.0 + specifier: 16.5.0 version: 16.5.0 - happy-dom: - specifier: ^20.3.3 - version: 20.8.4 + jsdom: + specifier: 26.1.0 + version: 26.1.0 prettier: - specifier: ^3.7.3 + specifier: 3.7.3 version: 3.7.3 react: - specifier: ^19.2.3 + specifier: 19.2.3 version: 19.2.3 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) typescript: specifier: 5.9.3 version: 5.9.3 typescript-eslint: - specifier: ^8.48.0 + specifier: 8.48.0 version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - practices/professional: + practices/standard-report: dependencies: '@ant-design/icons': specifier: 6.1.0 @@ -752,24 +834,24 @@ importers: '@visactor/vbi': specifier: workspace:* version: link:../../packages/vbi - '@visactor/vbi-react': - specifier: workspace:* - version: link:../../packages/vbi-react - '@visactor/vchart': - specifier: 2.0.15 - version: 2.0.15 '@visactor/vquery': specifier: workspace:* version: link:../../packages/vquery '@visactor/vseed': specifier: workspace:* version: link:../../packages/vseed - '@visactor/vtable': - specifier: 1.23.1 - version: 1.23.1 antd: specifier: 6.1.3 version: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: + specifier: 19.2.3 + version: 19.2.3 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + standard: + specifier: workspace:* + version: link:../standard zustand: specifier: 5.0.6 version: 5.0.6(@types/react@19.2.7)(react@19.2.3) @@ -795,9 +877,6 @@ importers: '@types/react': specifier: 19.2.7 version: 19.2.7 - '@types/react-dom': - specifier: 19.2.3 - version: 19.2.3(@types/react@19.2.7) eslint: specifier: 9.39.1 version: 9.39.1(jiti@2.6.1) @@ -810,12 +889,6 @@ importers: prettier: specifier: 3.7.3 version: 3.7.3 - react: - specifier: 19.2.3 - version: 19.2.3 - react-dom: - specifier: 19.2.3 - version: 19.2.3(react@19.2.3) typescript: specifier: 5.9.3 version: 5.9.3 diff --git a/practices/standard-report/.gitignore b/practices/standard-report/.gitignore new file mode 100644 index 0000000000..da7920ab50 --- /dev/null +++ b/practices/standard-report/.gitignore @@ -0,0 +1,15 @@ +# Local +.DS_Store +*.local +*.log* + +# Dist +node_modules +dist/ +tsconfig.tsbuildinfo +storybook-static + +# IDE +.vscode/* +!.vscode/extensions.json +.idea diff --git a/practices/demo/.prettierignore b/practices/standard-report/.prettierignore similarity index 100% rename from practices/demo/.prettierignore rename to practices/standard-report/.prettierignore diff --git a/practices/demo/.prettierrc b/practices/standard-report/.prettierrc similarity index 100% rename from practices/demo/.prettierrc rename to practices/standard-report/.prettierrc diff --git a/practices/standard-report/AGENTS.md b/practices/standard-report/AGENTS.md new file mode 100644 index 0000000000..2426d3eb77 --- /dev/null +++ b/practices/standard-report/AGENTS.md @@ -0,0 +1,28 @@ +# AGENTS.md + +You are an expert in JavaScript, Rspack, Rsbuild, Rslib, and practice-level frontend development. You write maintainable, performant, and accessible code. + +## Commands + +- `pnpm run build` - Build the library for production +- `pnpm run dev` - Turn on watch mode, watch for changes and rebuild the library + +## Docs + +- Rslib: https://rslib.rs/llms.txt +- Rsbuild: https://rsbuild.rs/llms.txt +- Rspack: https://rspack.rs/llms.txt + +## Tools + +### Rstest + +- Run `pnpm run test` to test your code + +### ESLint + +- Run `pnpm run lint` to lint your code + +### Prettier + +- Run `pnpm run format` to format your code diff --git a/practices/standard-report/README.md b/practices/standard-report/README.md new file mode 100644 index 0000000000..4c6bbe491a --- /dev/null +++ b/practices/standard-report/README.md @@ -0,0 +1,23 @@ +# standard-report + +## Setup + +Install the dependencies: + +```bash +pnpm install +``` + +## Get started + +Build the practice: + +```bash +pnpm run build +``` + +Build the practice in watch mode: + +```bash +pnpm run dev +``` diff --git a/practices/standard-report/docs/2026-03-24-standard-report/adr.md b/practices/standard-report/docs/2026-03-24-standard-report/adr.md new file mode 100644 index 0000000000..3903000d86 --- /dev/null +++ b/practices/standard-report/docs/2026-03-24-standard-report/adr.md @@ -0,0 +1,182 @@ +# ADR-002: practices/standard-report 报表分页与全屏编辑方案 + +## Status + +Proposed + +## Context + +`@visactor/vbi` 现在已经提供 `createReport(...)`、`VBIReportBuilder`、`reportBuilder.page.add/remove/update/get` 等能力,`practices/standard` 也已经有一套稳定的单图表可视化查询编辑器骨架。 + +这次目标不是继续在 `practices/standard` 里叠加报表能力,而是新建一个独立实践 `practices/standard-report`,用于验证以下组合场景: + +1. 报表默认存在 1 个 page。 +2. page 支持新增、删除、切换。 +3. 每个 page 只承载 1 个图表。 +4. 点击编辑后,进入全屏可视化查询编辑态。 +5. page 切换必须是横向翻页,具有幻灯片式转场。 + +现有 `practices/standard` 的核心工作台由 `FieldsPanel + ShelfPanel + ChartPanel + Toolbar` 组成,已经具备“编辑单个 `VBIChartBuilder`”的完整能力,因此标准报表实践的关键不是重写编辑器,而是决定: + +1. 报表页容器如何组织。 +2. page 管理与幻灯片切换如何分工。 +3. 全屏编辑是独立路由、独立草稿,还是直接绑定当前 page。 + +## Decision + +### 1. 新增独立实践 `practices/standard-report`,以 `practices/standard` 为代码基线 + +实现方式: + +1. 复制当前 `practices/standard` 为新的 `practices/standard-report`。 +2. `practices/standard` 保持单图表 practice 定位,不直接混入报表工作流。 +3. `standard-report` 内优先复用 `standard` 已有的编辑器组件、hook、store 和渲染面板。 +4. 新实践只补报表壳层、page 管理层和 page 内 chart 绑定层。 + +原因: + +1. 单图表 practice 和报表 practice 是两个不同交互模型,不应该在同一个 demo 里混合演进。 +2. 独立 practice 更适合验证 `VBI.createReport(...)` 的完整闭环。 +3. `standard` 已经是稳定编辑器基线,复制再裁剪的风险低于在原工程中条件分支扩展。 + +### 2. `VBIReportBuilder` 是唯一状态源,UI 只保存 `activePageId` 和编辑态 + +状态规则: + +1. 根状态使用 `VBI.createReport(...)` 创建的 `VBIReportBuilder`。 +2. UI 层只额外维护 `activePageId`、`editorOpen` 等视图状态。 +3. page 列表、page 标题、page 图表内容全部从 `reportBuilder.build().pages` 或 `reportBuilder.page.get(...)` 派生。 +4. page 切换以 `page.id` 为主键,不以数组 index 作为长期状态主键。 + +初始化规则: + +1. 报表初始化时如果没有 page,则立即创建 `Page 1`。 +2. 默认第一页直接作为当前激活页。 +3. 新增 page 时自动切到新 page。 +4. 删除当前 page 时,激活相邻 page;如果删到最后一个,则立即补回一个空 page。 + +原因: + +1. `page.id` 在增删场景下比 index 更稳定。 +2. 默认总有 1 个 page,可以避免空壳报表带来的大量边界分支。 +3. 直接围绕 `VBIReportBuilder` 建模,符合 Single Source of Truth。 + +### 3. 顶部 page 管理使用 `Tabs type="editable-card"`,内容区切换使用 `Carousel` + +组件分工: + +1. 顶部 page bar 使用 Ant Design `Tabs` 的 `editable-card` 模式。 +2. `Tabs` 负责 page 标题展示、激活态、增加、删除。 +3. 内容区使用 Ant Design `Carousel` 展示每个 page 的内容。 +4. `Tabs.onChange` 驱动 `Carousel` 切换;`Carousel.afterChange` 反向同步当前 page。 + +交互约束: + +1. `Tabs` 只承担 page 管理,不承担主内容渲染。 +2. `Carousel` 关闭自动播放,使用横向滚动切页。 +3. `Carousel` 开启 arrows,关闭 infinite。 +4. page 新增、点击、箭头切换都必须落到同一个 `activePageId`。 + +原因: + +1. Ant Design 文档明确 `Tabs` 的 card / editable-card 适合“管理可关闭视图”。 +2. Ant Design `Carousel` 天然提供横向切换与转场动画,更符合“像幻灯片一般”的要求。 +3. 单用 `Tabs` 无法很好表达幻灯片式切换;单用 `Carousel` 又缺少清晰的 add/remove 管理入口。 + +### 4. page 编辑使用全屏 `Drawer`,不切新路由,不开独立页面 + +组件决策: + +1. 点击 page 上的 `编辑` 按钮后,打开 `Drawer`。 +2. `Drawer` 使用全屏尺寸,作为 page 的沉浸式编辑态。 +3. `Drawer` 内复用 `practices/standard` 的现有工作台骨架。 +4. 关闭 `Drawer` 后返回报表视图,不丢失当前 page 上下文。 + +原因: + +1. Ant Design 文档明确 `Drawer` 适合“在不离开当前页面上下文的情况下处理较重子任务”。 +2. 报表编辑本质上是“编辑当前 page 的图表”这个子任务,不是跳转到另一个业务页面。 +3. 相比 `Modal`,`Drawer` 更适合承载完整字段面板、shelves、图表预览这类重编辑界面。 + +### 5. 全屏编辑直接绑定当前 `page.chart`,不维护第二份 chart draft + +实现约束: + +1. 打开编辑器时,通过当前 page 的 y-map 创建该 page 专属的 `VBIChartBuilder`。 +2. 编辑器内所有操作直接写入当前 `page.chart`。 +3. 关闭编辑器不需要额外“提交”步骤。 +4. 报表视图中的 page 预览天然和编辑结果保持一致。 + +明确不做: + +1. 不在 `standard-report` 里维护 `draftChartBuilder`。 +2. 不引入“打开编辑器时复制,点击保存再覆盖”的双写模型。 +3. 不新增 page 级 undo/commit 同步层。 + +原因: + +1. 当前实践目标是验证 report DSL 与 chart editor 的绑定链路,不是设计复杂草稿系统。 +2. 直接绑定 page.chart 可以保持唯一状态源。 +3. 双 builder 模型会引入额外的复制、覆盖、取消、冲突合并问题,超出本次范围。 + +### 6. 每个 page 只展示一个图表卡片,报表视图负责浏览,不负责完整编辑 + +页面布局: + +1. 每个 page 在报表视图中展示 `title + chart preview + edit action`。 +2. 报表视图只保留轻量浏览和翻页动作。 +3. 字段拖拽、shelves 配置、filter 编辑等重交互全部放到全屏编辑器中完成。 +4. page 内不再并排出现完整编辑器和完整报表浏览器。 + +原因: + +1. 目标已经明确“每个 page 内一个图表,点击编辑按钮全屏编辑”。 +2. 浏览态和编辑态分离后,报表体验更接近幻灯片。 +3. 这能最大化复用 `standard` 工作台,而不是在 report 页里再塞一套缩小版编辑器。 + +### 7. 本次标准报表实践只覆盖分页报表,不扩展多图布局和自由排版 + +范围约束: + +1. 一个 page 只允许一个 chart。 +2. 不支持 page 内多个 chart block。 +3. 不支持文本、注释、自由布局、拖拽排版。 +4. 不处理 report 导出、演示播放控制、主题模板。 + +原因: + +1. 当前目标是先验证 `createReport` 的最小可用报表交互。 +2. 多图布局会把问题从“分页报表”升级为“版面系统”。 +3. 先把“单页单图 + 翻页 + 全屏编辑”跑通,后续再扩展更合理。 + +### 8. 验证范围 + +必须覆盖以下验证: + +1. `practices/standard-report` 能独立启动和构建。 +2. 默认初始化后总是存在 1 个 page。 +3. `page.add()`、`page.remove()` 能正确驱动 `Tabs` 与 `Carousel` 同步更新。 +4. page 切换具有横向转场,不是瞬时替换。 +5. 点击 `编辑` 会打开全屏 `Drawer`。 +6. 编辑器内修改 chart 后,关闭 `Drawer` 返回报表视图能看到更新结果。 +7. 删除当前 page 后,激活页会自动落到合法 page。 +8. 删除到最后一页时,会自动补回空白第一页。 + +## Reference + +- `practices/standard/src/App/App.tsx` +- `@visactor/vbi` report builder API +- Ant Design Tabs: https://ant.design/llms-full.txt +- Ant Design Carousel: https://ant.design/llms-full.txt +- Ant Design Drawer: https://ant.design/llms-full.txt + +## 淘汰内容概述 + +本方案明确不采用以下做法: + +- 不在 `practices/standard` 原工程里直接混入 report 模式 +- 不使用 `Tabs` 单独承担 page 切换主动画 +- 不使用 `Carousel` 单独承担 page 的新增和删除管理 +- 不通过新路由跳走到单独 chart editor 页面 +- 不为 page.chart 维护第二份独立 draft builder +- 不在本次实践中扩展多图布局、富文本和自由排版 diff --git a/practices/demo/docs/todo2-standard-report/goal.md b/practices/standard-report/docs/2026-03-24-standard-report/goal.md similarity index 88% rename from practices/demo/docs/todo2-standard-report/goal.md rename to practices/standard-report/docs/2026-03-24-standard-report/goal.md index 81b43776c7..c96e0a9040 100644 --- a/practices/demo/docs/todo2-standard-report/goal.md +++ b/practices/standard-report/docs/2026-03-24-standard-report/goal.md @@ -6,7 +6,7 @@ status: in-progress 生成 architecture-decisions 文档, 命名为 ./adr.md -这里是 practices/demo 计划要完成的开发任务: +这里是 practices/standard 计划要完成的开发任务: [] 拷贝demo项目为 standard-report 项目实践进行改造 [] VBI 现已支持createReport 能力. [] 默认有一个page, 支持增加、删除page, [] 每个page内一个图表, 点击编辑按钮, 即可全屏编辑可视化查询. 支持横向翻页, 像幻灯片一般, 有转场动画.(standard-report) diff --git a/practices/standard-report/docs/2026-03-24-standard-report/plan.md b/practices/standard-report/docs/2026-03-24-standard-report/plan.md new file mode 100644 index 0000000000..e563873c2a --- /dev/null +++ b/practices/standard-report/docs/2026-03-24-standard-report/plan.md @@ -0,0 +1,133 @@ +# Plan: practices/standard-report 报表分页与全屏编辑落地 + +## Phase 1: 建立 `practices/standard-report` 工程骨架 + +目标:从 `practices/standard` 复制出独立 practice,并完成 workspace 接入。 + +任务: + +1. 复制 `practices/standard` 为 `practices/standard-report` +2. 修改新 practice 的 `package.json` 包名、README、docs README +3. 更新根工作区引用、`pnpm-lock.yaml`、需要消费该 practice 的 app 依赖和 tsconfig references +4. 保证 `practices/standard` 与 `practices/standard-report` 可并存,不互相覆盖 + +交付物: + +- 新目录 `practices/standard-report` +- 新 workspace 包名与依赖链路可解析 + +## Phase 2: 引入 report 根状态与 page 生命周期 + +目标:把单图表 builder 初始化切换为 report builder 初始化。 + +任务: + +1. 新增 `standard-report` 专用 store 或在现有 store 上拆出 report 版本 +2. 根状态改为 `VBI.createReport(...)` +3. 实现默认 1 个 page 的初始化逻辑 +4. 增加 `activePageId`、`editorOpen` 等视图状态 +5. 封装 `addPage`、`removePage`、`setActivePage`、`ensureAtLeastOnePage` 等动作 + +约束: + +1. `VBIReportBuilder` 是唯一状态源 +2. page 主键必须使用 `page.id` +3. 删除最后一页时必须自动补回空白第一页 + +交付物: + +- report store / hooks +- page 生命周期动作 + +## Phase 3: 搭建报表浏览视图 + +目标:让 practice 从“单图表编辑器”变成“报表分页浏览器”。 + +任务: + +1. 顶部实现 `Tabs type="editable-card"` page 管理栏 +2. 内容区接入 `Carousel`,按 page 渲染横向滑动卡片 +3. 每个 page 卡片展示标题、图表预览、编辑按钮 +4. 实现 `Tabs` 与 `Carousel` 的双向同步 +5. 处理新增 page 后自动激活、删除 page 后自动回落到相邻 page + +约束: + +1. 报表视图只负责浏览,不嵌入完整编辑器 +2. 每个 page 只显示 1 个 chart preview +3. page 切换必须保留横向转场动画 + +交付物: + +- report page tabs +- report carousel view +- page preview card + +## Phase 4: 复用 `standard` 工作台实现全屏编辑 + +目标:点击 page 编辑后,进入全屏图表编辑态。 + +任务: + +1. 抽出 `standard` 当前工作台骨架,形成可接收外部 chart builder 的复用入口 +2. 在 `standard-report` 中接入全屏 `Drawer` +3. 打开编辑器时,绑定当前 `page.chart` 对应的 `VBIChartBuilder` +4. 关闭编辑器后返回报表浏览态 +5. 确保编辑结果实时反映到 page preview + +约束: + +1. 不新建第二份 draft chart builder +2. 不新增保存/提交按钮作为唯一写入入口 +3. 所有编辑直接落到当前 page.chart + +交付物: + +- 可复用的 chart workbench +- report drawer editor + +## Phase 5: 处理标题、空态与边界交互 + +目标:补齐可用性和关键边界条件。 + +任务: + +1. page 默认命名为 `Page 1`、`Page 2` 等 +2. 支持 page 标题重命名 +3. 新 page 默认生成空 chart,并可直接进入编辑 +4. 无有效 preview 时展示空态卡片 +5. 删除当前 page、删除非当前 page、连续新增 page 的交互保持稳定 + +交付物: + +- page title 策略 +- empty state +- page edge-case handling + +## Phase 6: 测试与验证 + +目标:确保新 practice 和报表交互闭环可运行。 + +任务: + +1. 为 report store/page actions 增加测试 +2. 为默认 1 page、add/remove、active page fallback 增加测试 +3. 为 `Tabs + Carousel` 同步行为增加测试 +4. 为全屏 `Drawer` 打开/关闭与编辑结果回写增加测试 +5. 运行 `standard-report` 的 `typecheck`、`lint`、`test` +6. 如网站或其他 app 需要接入,再补对应引用验证 + +建议验证顺序: + +1. `pnpm --filter=standard-report run typecheck` +2. `pnpm --filter=standard-report run lint` +3. `pnpm --filter=standard-report run test` +4. 必要时执行一次根目录 `pnpm run typecheck` + +## 里程碑 + +1. M1: `standard-report` 工程可独立安装、构建、启动 +2. M2: 默认 1 page,支持 add/remove/switch +3. M3: page 浏览视图具备横向幻灯片切换 +4. M4: 点击编辑后进入全屏 `Drawer`,并直接编辑当前 page.chart +5. M5: 测试与类型检查通过 diff --git a/practices/standard-report/docs/README.md b/practices/standard-report/docs/README.md new file mode 100644 index 0000000000..00a3f76add --- /dev/null +++ b/practices/standard-report/docs/README.md @@ -0,0 +1,7 @@ +# practices/standard-report/docs + +本目录存放 `practices/standard-report` 的目标、ADR 与执行计划。 + +- 一个主题一个目录:`YYYY-MM-DD-topic/` +- 文件按需创建:`goal.md`、`adr.md`、`plan.md` +- `src/` 放示例实现,`docs/` 放设计与演进记录 diff --git a/practices/demo/eslint.config.mjs b/practices/standard-report/eslint.config.mjs similarity index 100% rename from practices/demo/eslint.config.mjs rename to practices/standard-report/eslint.config.mjs diff --git a/practices/standard-report/package.json b/practices/standard-report/package.json new file mode 100644 index 0000000000..8227913a11 --- /dev/null +++ b/practices/standard-report/package.json @@ -0,0 +1,55 @@ +{ + "name": "standard-report", + "version": "1.0.0", + "type": "module", + "exports": { + ".": { + "source": "./src/index.tsx", + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "rslib build", + "dev": "rslib build --watch", + "format": "prettier --write .", + "lint": "eslint .", + "test": "rstest", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@ant-design/icons": "6.1.0", + "@visactor/vbi": "workspace:*", + "@visactor/vquery": "workspace:*", + "@visactor/vseed": "workspace:*", + "antd": "6.1.3", + "react": "19.2.3", + "react-dom": "19.2.3", + "standard": "workspace:*", + "zustand": "5.0.6" + }, + "devDependencies": { + "@eslint/js": "9.39.1", + "@rsbuild/plugin-react": "1.4.2", + "@rslib/core": "0.18.6", + "@rstest/core": "0.8.3", + "@testing-library/jest-dom": "6.9.1", + "@testing-library/react": "16.3.1", + "@types/react": "19.2.7", + "eslint": "9.39.1", + "globals": "16.5.0", + "jsdom": "26.1.0", + "prettier": "3.7.3", + "typescript": "5.9.3", + "typescript-eslint": "8.48.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "private": true +} diff --git a/practices/demo/rslib.config.ts b/practices/standard-report/rslib.config.ts similarity index 100% rename from practices/demo/rslib.config.ts rename to practices/standard-report/rslib.config.ts diff --git a/practices/demo/rstest.config.ts b/practices/standard-report/rstest.config.ts similarity index 100% rename from practices/demo/rstest.config.ts rename to practices/standard-report/rstest.config.ts diff --git a/practices/demo/rstest.setup.ts b/practices/standard-report/rstest.setup.ts similarity index 100% rename from practices/demo/rstest.setup.ts rename to practices/standard-report/rstest.setup.ts diff --git a/practices/standard-report/src/App/App.tsx b/practices/standard-report/src/App/App.tsx new file mode 100644 index 0000000000..9a1410c1f7 --- /dev/null +++ b/practices/standard-report/src/App/App.tsx @@ -0,0 +1,25 @@ +import type { VBIReportBuilder } from '@visactor/vbi'; +import { Spin } from 'antd'; +import { useEffect } from 'react'; +import { useReportStore } from 'src/model'; +import { ReportWorkbench } from './layout/ReportWorkbench'; +import './styles/index.css'; + +type AppProps = { + builder?: VBIReportBuilder; +}; + +export const APP = ({ builder }: AppProps) => { + const initialize = useReportStore((state) => state.initialize); + const initialized = useReportStore((state) => state.initialized); + + useEffect(() => { + return initialize(builder); + }, [builder, initialize]); + + if (!initialized) { + return ; + } + + return ; +}; diff --git a/practices/standard-report/src/App/components/editor/ReportEditorDrawer.tsx b/practices/standard-report/src/App/components/editor/ReportEditorDrawer.tsx new file mode 100644 index 0000000000..631f411c7b --- /dev/null +++ b/practices/standard-report/src/App/components/editor/ReportEditorDrawer.tsx @@ -0,0 +1,52 @@ +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { APP as StandardAPP } from 'standard'; +import { Button, Drawer } from 'antd'; +import { useReportStore } from 'src/model'; + +export const ReportEditorDrawer = () => { + const open = useReportStore((state) => state.editorOpen); + const pageId = useReportStore((state) => state.activePageId); + const report = useReportStore((state) => state.report); + const reportBuilder = useReportStore((state) => state.reportBuilder); + const closeEditor = useReportStore((state) => state.closeEditor); + const pageBuilder = pageId ? reportBuilder.page.get(pageId) : undefined; + const page = report.pages.find((item) => item.id === pageId); + + return ( + +
+
+
+ +
+
+ Chart Editor +

+ {page?.title || '编辑图表'} +

+
+
+ +
+ {pageBuilder ? : null} +
+
+
+ ); +}; diff --git a/practices/standard-report/src/App/components/page/PageHoverActions.tsx b/practices/standard-report/src/App/components/page/PageHoverActions.tsx new file mode 100644 index 0000000000..a1a95798cd --- /dev/null +++ b/practices/standard-report/src/App/components/page/PageHoverActions.tsx @@ -0,0 +1,69 @@ +import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'; +import { Button, Tooltip } from 'antd'; + +type PageHoverActionsProps = { + canRemove: boolean; + showPlaceholder: boolean; + onAddPage: () => void; + onEdit: () => void; + onRemovePage: () => void; +}; + +const getActions = ({ + canRemove, + onAddPage, + onEdit, + onRemovePage, + showPlaceholder, +}: PageHoverActionsProps) => { + return [ + { + icon: , + key: 'edit', + label: showPlaceholder ? '添加当前图表' : '编辑当前图表', + onClick: onEdit, + }, + { + icon: , + key: 'add', + label: '新增一页', + onClick: onAddPage, + }, + ...(canRemove + ? [ + { + icon: , + key: 'remove', + label: '删除当前页', + onClick: onRemovePage, + }, + ] + : []), + ]; +}; + +export const PageHoverActions = (props: PageHoverActionsProps) => { + const actions = getActions(props); + + return ( +
+ {actions.map((action) => ( + +
+ ); +}; diff --git a/practices/standard-report/src/App/components/page/PageInsight.tsx b/practices/standard-report/src/App/components/page/PageInsight.tsx new file mode 100644 index 0000000000..1e7172be9d --- /dev/null +++ b/practices/standard-report/src/App/components/page/PageInsight.tsx @@ -0,0 +1,10 @@ +export const PageInsight = () => { + return ( +