@praxisui/charts 9.0.0-beta.85 → 9.0.0-beta.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/component-registry.json +231 -16
- package/fesm2022/praxisui-charts.mjs +17 -19
- package/package.json +3 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-07-
|
|
3
|
+
"generatedAt": "2026-07-23T02:50:28.389Z",
|
|
4
4
|
"packageName": "@praxisui/charts",
|
|
5
|
-
"packageVersion": "9.0.0-beta.
|
|
5
|
+
"packageVersion": "9.0.0-beta.88",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 2,
|
|
@@ -1186,7 +1186,49 @@
|
|
|
1186
1186
|
"type": "string",
|
|
1187
1187
|
"pattern": "^/(?!/)"
|
|
1188
1188
|
},
|
|
1189
|
-
"operation": {
|
|
1189
|
+
"operation": {
|
|
1190
|
+
"enum": [
|
|
1191
|
+
"group-by",
|
|
1192
|
+
"timeseries",
|
|
1193
|
+
"distribution",
|
|
1194
|
+
"comparison"
|
|
1195
|
+
]
|
|
1196
|
+
},
|
|
1197
|
+
"comparisonPeriod": {
|
|
1198
|
+
"type": "object",
|
|
1199
|
+
"required": [
|
|
1200
|
+
"field",
|
|
1201
|
+
"timezone",
|
|
1202
|
+
"preset",
|
|
1203
|
+
"mode"
|
|
1204
|
+
],
|
|
1205
|
+
"properties": {
|
|
1206
|
+
"field": {
|
|
1207
|
+
"type": "string"
|
|
1208
|
+
},
|
|
1209
|
+
"timezone": {
|
|
1210
|
+
"type": "string"
|
|
1211
|
+
},
|
|
1212
|
+
"preset": {
|
|
1213
|
+
"enum": [
|
|
1214
|
+
"TODAY",
|
|
1215
|
+
"YESTERDAY",
|
|
1216
|
+
"LAST_7_DAYS",
|
|
1217
|
+
"LAST_30_DAYS",
|
|
1218
|
+
"THIS_MONTH",
|
|
1219
|
+
"LAST_MONTH",
|
|
1220
|
+
"THIS_QUARTER",
|
|
1221
|
+
"THIS_YEAR"
|
|
1222
|
+
]
|
|
1223
|
+
},
|
|
1224
|
+
"mode": {
|
|
1225
|
+
"enum": [
|
|
1226
|
+
"PREVIOUS_ALIGNED",
|
|
1227
|
+
"PREVIOUS_CALENDAR_PERIOD"
|
|
1228
|
+
]
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
},
|
|
1190
1232
|
"dimensions": {
|
|
1191
1233
|
"type": "array",
|
|
1192
1234
|
"items": {
|
|
@@ -1300,7 +1342,49 @@
|
|
|
1300
1342
|
"type": "string",
|
|
1301
1343
|
"pattern": "^/(?!/)"
|
|
1302
1344
|
},
|
|
1303
|
-
"operation": {
|
|
1345
|
+
"operation": {
|
|
1346
|
+
"enum": [
|
|
1347
|
+
"group-by",
|
|
1348
|
+
"timeseries",
|
|
1349
|
+
"distribution",
|
|
1350
|
+
"comparison"
|
|
1351
|
+
]
|
|
1352
|
+
},
|
|
1353
|
+
"comparisonPeriod": {
|
|
1354
|
+
"type": "object",
|
|
1355
|
+
"required": [
|
|
1356
|
+
"field",
|
|
1357
|
+
"timezone",
|
|
1358
|
+
"preset",
|
|
1359
|
+
"mode"
|
|
1360
|
+
],
|
|
1361
|
+
"properties": {
|
|
1362
|
+
"field": {
|
|
1363
|
+
"type": "string"
|
|
1364
|
+
},
|
|
1365
|
+
"timezone": {
|
|
1366
|
+
"type": "string"
|
|
1367
|
+
},
|
|
1368
|
+
"preset": {
|
|
1369
|
+
"enum": [
|
|
1370
|
+
"TODAY",
|
|
1371
|
+
"YESTERDAY",
|
|
1372
|
+
"LAST_7_DAYS",
|
|
1373
|
+
"LAST_30_DAYS",
|
|
1374
|
+
"THIS_MONTH",
|
|
1375
|
+
"LAST_MONTH",
|
|
1376
|
+
"THIS_QUARTER",
|
|
1377
|
+
"THIS_YEAR"
|
|
1378
|
+
]
|
|
1379
|
+
},
|
|
1380
|
+
"mode": {
|
|
1381
|
+
"enum": [
|
|
1382
|
+
"PREVIOUS_ALIGNED",
|
|
1383
|
+
"PREVIOUS_CALENDAR_PERIOD"
|
|
1384
|
+
]
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
},
|
|
1304
1388
|
"dimensions": {
|
|
1305
1389
|
"type": "array"
|
|
1306
1390
|
},
|
|
@@ -2287,6 +2371,7 @@
|
|
|
2287
2371
|
"request": "Save this chart in the editor and reopen it without changing the runtime chart document.",
|
|
2288
2372
|
"operationId": "chart.document.set",
|
|
2289
2373
|
"params": {
|
|
2374
|
+
"version": "0.1.0",
|
|
2290
2375
|
"kind": "bar",
|
|
2291
2376
|
"source": {
|
|
2292
2377
|
"kind": "derived"
|
|
@@ -2352,6 +2437,136 @@
|
|
|
2352
2437
|
{
|
|
2353
2438
|
"chunkIndex": 1,
|
|
2354
2439
|
"chunkKind": "authoring_manifest",
|
|
2440
|
+
"content": "{\"operationId\":\"chart.document.set\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Set chart document Effects: set-value. Save this chart in the editor and reopen it without changing the runtime chart document.\",\"scope\":\"global\",\"target\":{\"kind\":\"dataBinding\",\"resolver\":\"x-ui-chart-document-root\",\"required\":false},\"inputContract\":{\"required\":[\"version\",\"kind\",\"source\"],\"properties\":[\"version\",\"kind\",\"chartId\",\"title\",\"subtitle\",\"source\",\"dimensions\",\"metrics\",\"events\"],\"semanticTerms\":[\"version\",\"kind\",\"chartId\",\"title\",\"subtitle\",\"source\",\"dimensions\",\"metrics\",\"events\",\"0.1.0\",\"bar\",\"combo\",\"horizontal-bar\",\"line\",\"pie\",\"donut\",\"area\",\"stacked-bar\",\"stacked-area\",\"scatter\",\"source.kind\",\"source.resource\",\"source.operation\",\"source.options\",\"praxis.stats\",\"derived\",\"group-by\",\"timeseries\",\"distribution\",\"comparison\",\"source.options.comparisonPeriod\",\"source.options.comparisonPeriod.field\",\"source.options.comparisonPeriod.timezone\",\"source.options.comparisonPeriod.preset\",\"source.options.comparisonPeriod.mode\",\"TODAY\",\"YESTERDAY\",\"LAST_7_DAYS\",\"LAST_30_DAYS\",\"THIS_MONTH\",\"LAST_MONTH\",\"THIS_QUARTER\",\"THIS_YEAR\",\"PREVIOUS_ALIGNED\",\"PREVIOUS_CALENDAR_PERIOD\"]},\"effects\":[\"set-value\"],\"affectedPaths\":[\"chartDocument\"],\"validators\":[{\"validatorId\":\"chart-document-shape\",\"level\":\"error\",\"code\":\"CHART_DOCUMENT_SHAPE\",\"description\":\"Document must conform to PraxisXUiChartContract.\"},{\"validatorId\":\"chart-version-supported\",\"level\":\"error\",\"code\":\"CHART_VERSION_SUPPORTED\"},{\"validatorId\":\"chart-type-series-axis-compatible\",\"level\":\"error\",\"code\":\"CHART_TYPE_SERIES_AXIS_COMPATIBLE\",\"description\":\"Chart kind, dimensions, metrics and axes must be compatible.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"config-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"round-trip-editor-runtime\",\"request\":\"Save this chart in the editor and reopen it without changing the runtime chart document.\",\"operationId\":\"chart.document.set\",\"params\":{\"version\":\"0.1.0\",\"kind\":\"bar\",\"source\":{\"kind\":\"derived\"},\"dimensions\":[{\"field\":\"department\"}],\"metrics\":[{\"field\":\"total\",\"aggregation\":\"count\"}]},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2441
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2442
|
+
"contentHash": "a895ab1d26033523020a3ec5feafd87b93af43774fcb21e9fb39753486e882f5",
|
|
2443
|
+
"sourceKind": "component_definition",
|
|
2444
|
+
"sourceId": "praxis-chart",
|
|
2445
|
+
"corpusVersion": "1.0.0"
|
|
2446
|
+
},
|
|
2447
|
+
{
|
|
2448
|
+
"chunkIndex": 2,
|
|
2449
|
+
"chunkKind": "authoring_manifest",
|
|
2450
|
+
"content": "{\"operationId\":\"chart.type.set\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Set chart type Effects: merge-object. Use a bar chart for employees by department.\",\"scope\":\"global\",\"target\":{\"kind\":\"chartType\",\"resolver\":\"x-ui-chart-kind\",\"required\":false},\"inputContract\":{\"required\":[\"kind\"],\"properties\":[\"kind\",\"orientation\"],\"semanticTerms\":[\"kind\",\"orientation\",\"bar\",\"combo\",\"horizontal-bar\",\"line\",\"pie\",\"donut\",\"area\",\"stacked-bar\",\"stacked-area\",\"scatter\",\"vertical\",\"horizontal\"]},\"effects\":[\"merge-object\"],\"affectedPaths\":[\"chartDocument.kind\",\"chartDocument.orientation\",\"chartDocument.metrics[]\",\"chartDocument.dimensions[]\"],\"validators\":[{\"validatorId\":\"chart-type-supported\",\"level\":\"error\",\"code\":\"CHART_TYPE_SUPPORTED\",\"description\":\"Chart kind must be supported by @praxisui/charts.\"},{\"validatorId\":\"chart-type-series-axis-compatible\",\"level\":\"error\",\"code\":\"CHART_TYPE_SERIES_AXIS_COMPATIBLE\",\"description\":\"Chart kind, dimensions, metrics and axes must be compatible.\"},{\"validatorId\":\"pie-single-metric\",\"level\":\"error\",\"code\":\"PIE_SINGLE_METRIC\",\"description\":\"Pie and donut charts support a single metric.\"},{\"validatorId\":\"combo-minimum-series\",\"level\":\"error\",\"code\":\"COMBO_MINIMUM_SERIES\",\"description\":\"Combo charts require at least two metrics.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"config-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"set-bar-chart\",\"request\":\"Use a bar chart for employees by department.\",\"operationId\":\"chart.type.set\",\"params\":{\"kind\":\"bar\"},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2451
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2452
|
+
"contentHash": "5b29a91d73804fc724e5a67e97c4fa7c5e084d0515e840eca9d8c233a971387d",
|
|
2453
|
+
"sourceKind": "component_definition",
|
|
2454
|
+
"sourceId": "praxis-chart",
|
|
2455
|
+
"corpusVersion": "1.0.0"
|
|
2456
|
+
},
|
|
2457
|
+
{
|
|
2458
|
+
"chunkIndex": 3,
|
|
2459
|
+
"chunkKind": "authoring_manifest",
|
|
2460
|
+
"content": "{\"operationId\":\"series.add\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Add chart series Effects: compile-domain-patch. Add salary as a summed metric.\",\"scope\":\"dataBinding\",\"target\":{\"kind\":\"series\",\"resolver\":\"x-ui-chart-metric-by-field\",\"required\":false},\"inputContract\":{\"required\":[\"field\"],\"properties\":[\"field\",\"label\",\"aggregation\",\"seriesKind\",\"axis\",\"color\",\"format\",\"afterField\"],\"semanticTerms\":[\"field\",\"label\",\"aggregation\",\"seriesKind\",\"axis\",\"color\",\"format\",\"afterField\",\"sum\",\"avg\",\"min\",\"max\",\"count\",\"distinct-count\",\"bar\",\"line\",\"area\",\"primary\",\"secondary\"]},\"effects\":[\"compile-domain-patch\"],\"affectedPaths\":[\"chartDocument.metrics[]\"],\"validators\":[{\"validatorId\":\"series-field-exists\",\"level\":\"error\",\"code\":\"SERIES_FIELD_EXISTS\",\"description\":\"Series field must exist in availableFields or schema context.\"},{\"validatorId\":\"series-field-aggregable\",\"level\":\"error\",\"code\":\"SERIES_FIELD_AGGREGABLE\",\"description\":\"Metric fields must support the requested aggregation.\"},{\"validatorId\":\"series-id-unique\",\"level\":\"error\",\"code\":\"SERIES_ID_UNIQUE\",\"description\":\"Metric/series identity must be stable and unique by field.\"},{\"validatorId\":\"chart-type-series-axis-compatible\",\"level\":\"error\",\"code\":\"CHART_TYPE_SERIES_AXIS_COMPATIBLE\",\"description\":\"Chart kind, dimensions, metrics and axes must be compatible.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"config-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"add-salary-series\",\"request\":\"Add salary as a summed metric.\",\"operationId\":\"series.add\",\"params\":{\"field\":\"salary\",\"aggregation\":\"sum\",\"label\":\"Salary\"},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2461
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2462
|
+
"contentHash": "e51af6df1bfa5436394cfb5b9271de9d1093e15bdf6ccace9dc1226b12df6cf2",
|
|
2463
|
+
"sourceKind": "component_definition",
|
|
2464
|
+
"sourceId": "praxis-chart",
|
|
2465
|
+
"corpusVersion": "1.0.0"
|
|
2466
|
+
},
|
|
2467
|
+
{
|
|
2468
|
+
"chunkIndex": 4,
|
|
2469
|
+
"chunkKind": "authoring_manifest",
|
|
2470
|
+
"content": "{\"operationId\":\"series.remove\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Remove chart series Effects: remove-by-key. Remove the old cost series. Remove total when more than one chart series resolves to total.\",\"scope\":\"dataBinding\",\"target\":{\"kind\":\"series\",\"resolver\":\"x-ui-chart-metric-by-field\",\"required\":true},\"inputContract\":{\"required\":[\"field\"],\"properties\":[\"field\",\"replacementField\"],\"semanticTerms\":[\"field\",\"replacementField\"]},\"effects\":[\"remove-by-key\"],\"affectedPaths\":[\"chartDocument.metrics[]\"],\"validators\":[{\"validatorId\":\"series-exists\",\"level\":\"error\",\"code\":\"SERIES_EXISTS\",\"description\":\"Series removal target must exist.\"},{\"validatorId\":\"destructive-series-removal-confirmed\",\"level\":\"error\",\"code\":\"DESTRUCTIVE_SERIES_REMOVAL_CONFIRMED\",\"description\":\"Removing a series requires explicit confirmation.\"},{\"validatorId\":\"chart-keeps-required-metric\",\"level\":\"error\",\"code\":\"CHART_KEEPS_REQUIRED_METRIC\",\"description\":\"A chart cannot be left without required metrics.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\",\"target-series-exists\",\"confirmation-collected\"],\"submissionImpact\":\"config-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"remove-old-series\",\"request\":\"Remove the old cost series.\",\"operationId\":\"series.remove\",\"target\":\"cost\",\"params\":{\"field\":\"cost\"},\"isPositive\":true}],\"negativeExamples\":[{\"id\":\"reject-ambiguous-series-target\",\"request\":\"Remove total when more than one chart series resolves to total.\",\"operationId\":\"series.remove\",\"target\":\"total\",\"params\":{\"field\":\"total\"},\"isPositive\":false}]}",
|
|
2471
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2472
|
+
"contentHash": "0351fc6af80d7fc17f890e2a86c7fa8f8e12309666880fde93785eb9f355d3f7",
|
|
2473
|
+
"sourceKind": "component_definition",
|
|
2474
|
+
"sourceId": "praxis-chart",
|
|
2475
|
+
"corpusVersion": "1.0.0"
|
|
2476
|
+
},
|
|
2477
|
+
{
|
|
2478
|
+
"chunkIndex": 5,
|
|
2479
|
+
"chunkKind": "authoring_manifest",
|
|
2480
|
+
"content": "{\"operationId\":\"axis.configure\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Configure chart axis Effects: compile-domain-patch. Put margin on a secondary axis for the combo chart.\",\"scope\":\"dataBinding\",\"target\":{\"kind\":\"axis\",\"resolver\":\"x-ui-chart-dimension-or-metric-axis\",\"required\":true},\"inputContract\":{\"required\":[],\"properties\":[\"dimensionField\",\"dimensionRole\",\"metricField\",\"metricAxis\",\"orientation\"],\"semanticTerms\":[\"dimensionField\",\"dimensionRole\",\"metricField\",\"metricAxis\",\"orientation\",\"category\",\"series\",\"segment\",\"time\",\"value\",\"primary\",\"secondary\",\"vertical\",\"horizontal\"]},\"effects\":[\"compile-domain-patch\"],\"affectedPaths\":[\"chartDocument.dimensions[]\",\"chartDocument.metrics[].axis\",\"chartDocument.orientation\"],\"validators\":[{\"validatorId\":\"axis-field-exists\",\"level\":\"error\",\"code\":\"AXIS_FIELD_EXISTS\",\"description\":\"Axis/dimension fields must exist in availableFields or schema context.\"},{\"validatorId\":\"secondary-axis-combo-only\",\"level\":\"error\",\"code\":\"SECONDARY_AXIS_COMBO_ONLY\",\"description\":\"Secondary axis is supported only for combo charts.\"},{\"validatorId\":\"cartesian-dimension-required\",\"level\":\"error\",\"code\":\"CARTESIAN_DIMENSION_REQUIRED\",\"description\":\"Cartesian charts require at least one dimension.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"config-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"configure-secondary-axis\",\"request\":\"Put margin on a secondary axis for the combo chart.\",\"operationId\":\"axis.configure\",\"params\":{\"metricField\":\"margin\",\"metricAxis\":\"secondary\"},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2481
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2482
|
+
"contentHash": "8b9ae691f0a3fe1147519e4d47c161a49a85a760b74d04fd5d62ec5b3dd94255",
|
|
2483
|
+
"sourceKind": "component_definition",
|
|
2484
|
+
"sourceId": "praxis-chart",
|
|
2485
|
+
"corpusVersion": "1.0.0"
|
|
2486
|
+
},
|
|
2487
|
+
{
|
|
2488
|
+
"chunkIndex": 6,
|
|
2489
|
+
"chunkKind": "authoring_manifest",
|
|
2490
|
+
"content": "{\"operationId\":\"data.resource.bind\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Bind chart data resource Effects: compile-domain-patch. Bind this chart to payroll stats grouped by department. Compare active employees by department with the previous aligned 30-day period. Use the prompt example text to decide which backend endpoint to call.\",\"scope\":\"dataBinding\",\"target\":{\"kind\":\"dataBinding\",\"resolver\":\"x-ui-chart-source-and-field-catalog\",\"required\":false},\"inputContract\":{\"required\":[\"sourceKind\"],\"properties\":[\"sourceKind\",\"resource\",\"operation\",\"comparisonPeriod\",\"dimensions\",\"metrics\"],\"semanticTerms\":[\"sourceKind\",\"resource\",\"operation\",\"comparisonPeriod\",\"dimensions\",\"metrics\",\"praxis.stats\",\"derived\",\"group-by\",\"timeseries\",\"distribution\",\"comparison\",\"comparisonPeriod.field\",\"comparisonPeriod.timezone\",\"comparisonPeriod.preset\",\"comparisonPeriod.mode\",\"TODAY\",\"YESTERDAY\",\"LAST_7_DAYS\",\"LAST_30_DAYS\",\"THIS_MONTH\",\"LAST_MONTH\",\"THIS_QUARTER\",\"THIS_YEAR\",\"PREVIOUS_ALIGNED\",\"PREVIOUS_CALENDAR_PERIOD\",\"dimensions.field\",\"dimensions.role\",\"metrics.field\",\"metrics.label\",\"metrics.aggregation\",\"metrics.seriesKind\",\"metrics.axis\",\"metrics.color\",\"metrics.format\",\"metrics.afterField\",\"sum\",\"avg\",\"min\",\"max\",\"count\",\"distinct-count\",\"bar\",\"line\",\"area\",\"primary\",\"secondary\"]},\"effects\":[\"compile-domain-patch\"],\"affectedPaths\":[\"chartDocument.source\",\"chartDocument.source.options.comparisonPeriod\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\"],\"validators\":[{\"validatorId\":\"remote-resource-in-api-metadata\",\"level\":\"error\",\"code\":\"REMOTE_RESOURCE_IN_API_METADATA\",\"description\":\"Remote praxis.stats resource must equal a governed root-relative availableResources[].path beginning with exactly one slash; a discovery id is not an executable resource path.\"},{\"validatorId\":\"bound-fields-exist\",\"level\":\"error\",\"code\":\"BOUND_FIELDS_EXIST\",\"description\":\"Bound dimensions and metrics must exist in the capability-backed field context and satisfy its operation, aggregation and distribution-mode eligibility.\"},{\"validatorId\":\"stats-operation-supported\",\"level\":\"error\",\"code\":\"STATS_OPERATION_SUPPORTED\",\"description\":\"Remote datasource operation must be supported by the stats backend.\"},{\"validatorId\":\"comparison-period-complete\",\"level\":\"error\",\"code\":\"COMPARISON_PERIOD_COMPLETE\",\"description\":\"Comparison requires a time-series eligible period field, timezone, preset and mode grounded in the governed resource capabilities.\"},{\"validatorId\":\"comparison-metrics-supported\",\"level\":\"error\",\"code\":\"COMPARISON_METRICS_SUPPORTED\",\"description\":\"Comparison requires unique metrics[] aliases using only COUNT, DISTINCT_COUNT or SUM capabilities; singular metric payloads are not authorable.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"affects-remote-binding\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"bind-stats-resource\",\"request\":\"Bind this chart to payroll stats grouped by department.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"/api/stats/payroll\",\"operation\":\"group-by\"},\"isPositive\":true},{\"id\":\"bind-comparison-resource\",\"request\":\"Compare active employees by department with the previous aligned 30-day period.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"/api/human-resources/employees\",\"operation\":\"comparison\",\"comparisonPeriod\":{\"field\":\"admissionDate\",\"timezone\":\"America/Sao_Paulo\",\"preset\":\"LAST_30_DAYS\",\"mode\":\"PREVIOUS_ALIGNED\"},\"dimensions\":[{\"field\":\"department\"}],\"metrics\":[{\"field\":\"employeeId\",\"aggregation\":\"distinct-count\"}]},\"isPositive\":true}],\"negativeExamples\":[{\"id\":\"reject-prompt-routing\",\"request\":\"Use the prompt example text to decide which backend endpoint to call.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"from prompt example\"},\"isPositive\":false}]}",
|
|
2491
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2492
|
+
"contentHash": "b1b985542c4a36a148674cc5cf41007e2b093e96b99ffcfe1550c24392160a5e",
|
|
2493
|
+
"sourceKind": "component_definition",
|
|
2494
|
+
"sourceId": "praxis-chart",
|
|
2495
|
+
"corpusVersion": "1.0.0"
|
|
2496
|
+
},
|
|
2497
|
+
{
|
|
2498
|
+
"chunkIndex": 7,
|
|
2499
|
+
"chunkKind": "authoring_manifest",
|
|
2500
|
+
"content": "{\"operationId\":\"queryContext.set\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Set chart query context Effects: set-value. Limit the chart to active employees and sort by total descending.\",\"scope\":\"dataBinding\",\"target\":{\"kind\":\"queryContext\",\"resolver\":\"praxis-chart-query-context\",\"required\":false},\"inputContract\":{\"required\":[],\"properties\":[\"filters\",\"filterExpression\",\"sort\",\"limit\",\"page\",\"meta\"],\"semanticTerms\":[\"filters\",\"filterExpression\",\"sort\",\"limit\",\"page\",\"meta\"]},\"effects\":[\"set-value\"],\"affectedPaths\":[\"queryContext\"],\"validators\":[{\"validatorId\":\"query-context-structured\",\"level\":\"error\",\"code\":\"QUERY_CONTEXT_STRUCTURED\",\"description\":\"queryContext must use the canonical @praxisui/core structure for filters, filterExpression, sort, limit, page and meta.\"},{\"validatorId\":\"query-context-filter-expression-stats-unsupported\",\"level\":\"error\",\"code\":\"QUERY_CONTEXT_FILTER_EXPRESSION_STATS_UNSUPPORTED\",\"description\":\"praxis.stats currently executes only flat queryContext.filters; governed filterExpression must be preserved for diagnostics and rejected until backend expression support is declared.\"},{\"validatorId\":\"query-context-fields-exist\",\"level\":\"warning\",\"code\":\"QUERY_CONTEXT_FIELDS_EXIST\",\"description\":\"queryContext filter fields should exist in the schema/data context.\"},{\"validatorId\":\"query-context-safe-values\",\"level\":\"error\",\"code\":\"QUERY_CONTEXT_SAFE_VALUES\",\"description\":\"queryContext values must be serializable safe data.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"affects-schema-backed-data\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"set-query-context\",\"request\":\"Limit the chart to active employees and sort by total descending.\",\"operationId\":\"queryContext.set\",\"params\":{\"filters\":{\"status\":\"ACTIVE\"},\"sort\":[\"total,desc\"],\"limit\":10},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2501
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2502
|
+
"contentHash": "20e8b3f6814614eb6fc695045e044d82d7deddfe3d972198a2997e23f2e99d17",
|
|
2503
|
+
"sourceKind": "component_definition",
|
|
2504
|
+
"sourceId": "praxis-chart",
|
|
2505
|
+
"corpusVersion": "1.0.0"
|
|
2506
|
+
},
|
|
2507
|
+
{
|
|
2508
|
+
"chunkIndex": 8,
|
|
2509
|
+
"chunkKind": "authoring_manifest",
|
|
2510
|
+
"content": "{\"operationId\":\"pointClick.configure\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Configure chart point click Effects: compile-domain-patch.\",\"scope\":\"eventMapping\",\"target\":{\"kind\":\"pointClick\",\"resolver\":\"x-ui-chart-events-point-click\",\"required\":false},\"inputContract\":{\"required\":[\"action\"],\"properties\":[\"action\",\"target\",\"mapping\"],\"semanticTerms\":[\"action\",\"target\",\"mapping\",\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"effects\":[\"compile-domain-patch\"],\"affectedPaths\":[\"chartDocument.events.pointClick\"],\"validators\":[{\"validatorId\":\"point-click-action-structured\",\"level\":\"error\",\"code\":\"POINT_CLICK_ACTION_STRUCTURED\",\"description\":\"Configured pointClick actions must be emitted through pointAction while pointClick remains raw point evidence.\"},{\"validatorId\":\"event-target-governed\",\"level\":\"error\",\"code\":\"EVENT_TARGET_GOVERNED\",\"description\":\"Event targets must resolve to exactly one availableTargets[].id and declare the authored event plus action; missing, duplicate or cross-event identities fail closed.\"},{\"validatorId\":\"event-mapping-fields-exist\",\"level\":\"error\",\"code\":\"EVENT_MAPPING_FIELDS_EXIST\",\"description\":\"Event mapping source fields must exist in chart data context; praxis.stats reserves key for bucket identity and label for presentation.\"},{\"validatorId\":\"event-action-supported\",\"level\":\"error\",\"code\":\"EVENT_ACTION_SUPPORTED\",\"description\":\"Event action must be one of the supported structured action kinds.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"affects-schema-backed-data\",\"presentationAffordances\":[],\"positiveExamples\":[],\"negativeExamples\":[]}",
|
|
2511
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2512
|
+
"contentHash": "eed9b338749b7d2488a93c6615dd25bba7f9c54b7f6692235503a31bb27b5f6a",
|
|
2513
|
+
"sourceKind": "component_definition",
|
|
2514
|
+
"sourceId": "praxis-chart",
|
|
2515
|
+
"corpusVersion": "1.0.0"
|
|
2516
|
+
},
|
|
2517
|
+
{
|
|
2518
|
+
"chunkIndex": 9,
|
|
2519
|
+
"chunkKind": "authoring_manifest",
|
|
2520
|
+
"content": "{\"operationId\":\"crossFilter.configure\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Configure chart cross-filter Effects: compile-domain-patch. Use selected department to filter the employee table. Use the governed department bucket identity to filter the employee table.\",\"scope\":\"eventMapping\",\"target\":{\"kind\":\"crossFilter\",\"resolver\":\"x-ui-chart-events-cross-filter\",\"required\":false},\"inputContract\":{\"required\":[\"action\"],\"properties\":[\"action\",\"target\",\"mapping\"],\"semanticTerms\":[\"action\",\"target\",\"mapping\",\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"effects\":[\"compile-domain-patch\"],\"affectedPaths\":[\"chartDocument.events.crossFilter\"],\"validators\":[{\"validatorId\":\"cross-filter-output-structured\",\"level\":\"error\",\"code\":\"CROSS_FILTER_OUTPUT_STRUCTURED\",\"description\":\"Cross-filter output must be structured as query-context filters.\"},{\"validatorId\":\"event-target-governed\",\"level\":\"error\",\"code\":\"EVENT_TARGET_GOVERNED\",\"description\":\"Event targets must resolve to exactly one availableTargets[].id and declare the authored event plus action; missing, duplicate or cross-event identities fail closed.\"},{\"validatorId\":\"event-mapping-fields-exist\",\"level\":\"error\",\"code\":\"EVENT_MAPPING_FIELDS_EXIST\",\"description\":\"Event mapping source fields must exist in chart data context; praxis.stats reserves key for bucket identity and label for presentation.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"affects-schema-backed-data\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"configure-cross-filter\",\"request\":\"Use selected department to filter the employee table.\",\"operationId\":\"crossFilter.configure\",\"params\":{\"action\":\"filter-widget\",\"target\":\"employeesTable\",\"mapping\":{\"department\":\"department\"}},\"isPositive\":true},{\"id\":\"configure-stats-key-cross-filter\",\"request\":\"Use the governed department bucket identity to filter the employee table.\",\"operationId\":\"crossFilter.configure\",\"params\":{\"action\":\"filter-widget\",\"target\":\"employeesTable\",\"mapping\":{\"key\":\"departmentIdsIn\"}},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2521
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2522
|
+
"contentHash": "45b2594fb67b35ce5c73771ddae7a8a8410b9b73132f07537f6e22d47b39ef7a",
|
|
2523
|
+
"sourceKind": "component_definition",
|
|
2524
|
+
"sourceId": "praxis-chart",
|
|
2525
|
+
"corpusVersion": "1.0.0"
|
|
2526
|
+
},
|
|
2527
|
+
{
|
|
2528
|
+
"chunkIndex": 10,
|
|
2529
|
+
"chunkKind": "authoring_manifest",
|
|
2530
|
+
"content": "{\"operationId\":\"drilldown.configure\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Configure chart drilldown Effects: compile-domain-patch. Open the department detail when a point is clicked. Navigate to javascript:alert(1) on drilldown.\",\"scope\":\"eventMapping\",\"target\":{\"kind\":\"drilldown\",\"resolver\":\"x-ui-chart-events-drill-down\",\"required\":false},\"inputContract\":{\"required\":[\"action\"],\"properties\":[\"action\",\"target\",\"mapping\"],\"semanticTerms\":[\"action\",\"target\",\"mapping\",\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"effects\":[\"compile-domain-patch\"],\"affectedPaths\":[\"chartDocument.events.drillDown\"],\"validators\":[{\"validatorId\":\"drilldown-target-governed\",\"level\":\"error\",\"code\":\"DRILLDOWN_TARGET_GOVERNED\",\"description\":\"Drilldown target must be a governed route or widget target.\"},{\"validatorId\":\"event-mapping-fields-exist\",\"level\":\"error\",\"code\":\"EVENT_MAPPING_FIELDS_EXIST\",\"description\":\"Event mapping source fields must exist in chart data context; praxis.stats reserves key for bucket identity and label for presentation.\"},{\"validatorId\":\"event-action-supported\",\"level\":\"error\",\"code\":\"EVENT_ACTION_SUPPORTED\",\"description\":\"Event action must be one of the supported structured action kinds.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"config-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"configure-drilldown\",\"request\":\"Open the department detail when a point is clicked.\",\"operationId\":\"drilldown.configure\",\"params\":{\"action\":\"navigate\",\"target\":\"/departments/detail\",\"mapping\":{\"department\":\"departmentId\"}},\"isPositive\":true}],\"negativeExamples\":[{\"id\":\"reject-unsafe-target\",\"request\":\"Navigate to javascript:alert(1) on drilldown.\",\"operationId\":\"drilldown.configure\",\"params\":{\"event\":\"drillDown\",\"action\":\"navigate\",\"target\":\"javascript:alert(1)\"},\"isPositive\":false}]}",
|
|
2531
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2532
|
+
"contentHash": "8097ba4243dad7bfa8e18833167bef7acc13f5b346e2a8e7740beda45a0b7667",
|
|
2533
|
+
"sourceKind": "component_definition",
|
|
2534
|
+
"sourceId": "praxis-chart",
|
|
2535
|
+
"corpusVersion": "1.0.0"
|
|
2536
|
+
},
|
|
2537
|
+
{
|
|
2538
|
+
"chunkIndex": 11,
|
|
2539
|
+
"chunkKind": "authoring_manifest",
|
|
2540
|
+
"content": "{\"operationId\":\"selection.configure\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Configure chart selection Effects: compile-domain-patch. Emit selected department as a structured selection event.\",\"scope\":\"selection\",\"target\":{\"kind\":\"selection\",\"resolver\":\"x-ui-chart-events-selection-change\",\"required\":false},\"inputContract\":{\"required\":[\"action\"],\"properties\":[\"action\",\"target\",\"mapping\"],\"semanticTerms\":[\"action\",\"target\",\"mapping\",\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"effects\":[\"compile-domain-patch\"],\"affectedPaths\":[\"chartDocument.events.selectionChange\"],\"validators\":[{\"validatorId\":\"selection-output-structured\",\"level\":\"error\",\"code\":\"SELECTION_OUTPUT_STRUCTURED\",\"description\":\"Selection output must resolve selected point filters deterministically.\"},{\"validatorId\":\"event-target-governed\",\"level\":\"error\",\"code\":\"EVENT_TARGET_GOVERNED\",\"description\":\"Event targets must resolve to exactly one availableTargets[].id and declare the authored event plus action; missing, duplicate or cross-event identities fail closed.\"},{\"validatorId\":\"event-mapping-fields-exist\",\"level\":\"error\",\"code\":\"EVENT_MAPPING_FIELDS_EXIST\",\"description\":\"Event mapping source fields must exist in chart data context; praxis.stats reserves key for bucket identity and label for presentation.\"},{\"validatorId\":\"event-action-supported\",\"level\":\"error\",\"code\":\"EVENT_ACTION_SUPPORTED\",\"description\":\"Event action must be one of the supported structured action kinds.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"affects-schema-backed-data\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"configure-selection\",\"request\":\"Emit selected department as a structured selection event.\",\"operationId\":\"selection.configure\",\"params\":{\"action\":\"emit\",\"mapping\":{\"department\":\"department\"}},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2541
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2542
|
+
"contentHash": "ce5040ad78883394cfd77e9217b3ed48a7a313629bda29fdce83059e82d54402",
|
|
2543
|
+
"sourceKind": "component_definition",
|
|
2544
|
+
"sourceId": "praxis-chart",
|
|
2545
|
+
"corpusVersion": "1.0.0"
|
|
2546
|
+
},
|
|
2547
|
+
{
|
|
2548
|
+
"chunkIndex": 12,
|
|
2549
|
+
"chunkKind": "authoring_manifest",
|
|
2550
|
+
"content": "{\"operationId\":\"legend.configure\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Configure chart legend Effects: set-value. Show the legend.\",\"scope\":\"skin\",\"target\":{\"kind\":\"legend\",\"resolver\":\"x-ui-chart-legend-feature\",\"required\":false},\"inputContract\":{\"required\":[\"enabled\"],\"properties\":[\"enabled\"],\"semanticTerms\":[\"enabled\"]},\"effects\":[\"set-value\"],\"affectedPaths\":[\"chartDocument.legend\"],\"validators\":[{\"validatorId\":\"feature-toggle-valid\",\"level\":\"error\",\"code\":\"FEATURE_TOGGLE_VALID\",\"description\":\"Legend and tooltip feature values must be boolean or toggleable feature objects.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"visual-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"toggle-legend\",\"request\":\"Show the legend.\",\"operationId\":\"legend.configure\",\"params\":{\"enabled\":true},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2551
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2552
|
+
"contentHash": "3ee7bd2ac4dceda1f40df5c2781b8c14444ddfa10ee7e68d0cb8986e1fa52e9c",
|
|
2553
|
+
"sourceKind": "component_definition",
|
|
2554
|
+
"sourceId": "praxis-chart",
|
|
2555
|
+
"corpusVersion": "1.0.0"
|
|
2556
|
+
},
|
|
2557
|
+
{
|
|
2558
|
+
"chunkIndex": 13,
|
|
2559
|
+
"chunkKind": "authoring_manifest",
|
|
2560
|
+
"content": "{\"operationId\":\"tooltip.configure\",\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operation_card\",\"semanticSummary\":\"Configure chart tooltip Effects: set-value. Disable tooltips.\",\"scope\":\"skin\",\"target\":{\"kind\":\"tooltip\",\"resolver\":\"x-ui-chart-tooltip-feature\",\"required\":false},\"inputContract\":{\"required\":[\"enabled\"],\"properties\":[\"enabled\"],\"semanticTerms\":[\"enabled\"]},\"effects\":[\"set-value\"],\"affectedPaths\":[\"chartDocument.tooltip\"],\"validators\":[{\"validatorId\":\"feature-toggle-valid\",\"level\":\"error\",\"code\":\"FEATURE_TOGGLE_VALID\",\"description\":\"Legend and tooltip feature values must be boolean or toggleable feature objects.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}],\"preconditions\":[\"config-initialized\"],\"submissionImpact\":\"visual-only\",\"presentationAffordances\":[],\"positiveExamples\":[{\"id\":\"toggle-tooltip\",\"request\":\"Disable tooltips.\",\"operationId\":\"tooltip.configure\",\"params\":{\"enabled\":false},\"isPositive\":true}],\"negativeExamples\":[]}",
|
|
2561
|
+
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2562
|
+
"contentHash": "2daea33f3dd8ff521d400edf0ff7b8659fca5696a4c6babad9613e8a9abd9561",
|
|
2563
|
+
"sourceKind": "component_definition",
|
|
2564
|
+
"sourceId": "praxis-chart",
|
|
2565
|
+
"corpusVersion": "1.0.0"
|
|
2566
|
+
},
|
|
2567
|
+
{
|
|
2568
|
+
"chunkIndex": 14,
|
|
2569
|
+
"chunkKind": "authoring_manifest",
|
|
2355
2570
|
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"identity\"}",
|
|
2356
2571
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2357
2572
|
"contentHash": "d481e91e447ae5f92b0d3ffb171cf993e3b16409be95ce237464248c9e5c953b",
|
|
@@ -2360,7 +2575,7 @@
|
|
|
2360
2575
|
"corpusVersion": "1.0.0"
|
|
2361
2576
|
},
|
|
2362
2577
|
{
|
|
2363
|
-
"chunkIndex":
|
|
2578
|
+
"chunkIndex": 15,
|
|
2364
2579
|
"chunkKind": "authoring_manifest",
|
|
2365
2580
|
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"runtimeInputs\",\"runtimeInputs\":[{\"name\":\"chartDocument\",\"type\":\"PraxisXUiChartContract | null\",\"description\":\"Canonical x-ui.chart document used as the authoring source of truth.\"},{\"name\":\"config\",\"type\":\"PraxisChartConfig\",\"description\":\"Runtime chart configuration mapped from or supplied beside the canonical chart document.\"},{\"name\":\"queryContext\",\"type\":\"PraxisChartQueryContext | null\",\"description\":\"Canonical @praxisui/core query context observed by remote datasource requests; flat filters execute in praxis.stats and governed filterExpression is preserved but rejected until stats expression support is proven.\"},{\"name\":\"remoteDataResolver\",\"type\":\"PraxisChartRemoteDataResolver | null\",\"description\":\"Host-governed remote row resolver for integrations outside the default praxis.stats client.\"},{\"name\":\"availableResources\",\"type\":\"ChartEditorResourceOption[]\",\"description\":\"Transient authoring catalog: id is the stable /schemas/catalog discovery identity, while path is the governed root-relative operational value persisted to chartDocument.source.resource.\"},{\"name\":\"availableFields\",\"type\":\"ChartEditorFieldOption[]\",\"description\":\"Transient capability-backed field catalog. operations, aggregations and distributionModes declare exact group-by, timeseries, distribution/terms and distribution/histogram eligibility.\"},{\"name\":\"availableTargets\",\"type\":\"ChartEditorTargetOption[]\",\"description\":\"Transient visual-authoring targets derived from existing top-level composition links to public, non-deprecated widget ports or explicitly writable declared page state; id is the unique authoring identity and events[] declares the exact structured Chart events authorized by compatible links. Raw pointClick evidence never authorizes events.pointClick, which is emitted through pointAction. Local transform.output semantic-kind inspection does not replace a Core-validated transform-output projection (P2), and the visual catalog does not yet publish the target input-schema/port projection required for end-to-end mapping validation.\"}]}",
|
|
2366
2581
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
@@ -2370,7 +2585,7 @@
|
|
|
2370
2585
|
"corpusVersion": "1.0.0"
|
|
2371
2586
|
},
|
|
2372
2587
|
{
|
|
2373
|
-
"chunkIndex":
|
|
2588
|
+
"chunkIndex": 16,
|
|
2374
2589
|
"chunkKind": "authoring_manifest",
|
|
2375
2590
|
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"editableTargets\",\"editableTargets\":[{\"kind\":\"chartType\",\"resolver\":\"x-ui-chart-kind\",\"description\":\"Canonical `kind` field in PraxisXUiChartContract.\"},{\"kind\":\"series\",\"resolver\":\"x-ui-chart-metric-by-field\",\"description\":\"Metric-backed series definition in `metrics[]`, keyed by field.\"},{\"kind\":\"axis\",\"resolver\":\"x-ui-chart-dimension-or-metric-axis\",\"description\":\"Dimension role, metric axis and chart orientation semantics.\"},{\"kind\":\"dataBinding\",\"resolver\":\"x-ui-chart-source-and-field-catalog\",\"description\":\"Source, dimensions, metrics and resource/field bindings.\"},{\"kind\":\"queryContext\",\"resolver\":\"praxis-chart-query-context\",\"description\":\"Runtime @praxisui/core queryContext contract for filters, filterExpression, sort, limit, page and governance metadata.\"},{\"kind\":\"pointClick\",\"resolver\":\"x-ui-chart-events-point-click\",\"description\":\"Raw pointClick evidence plus a separate pointAction output for configured pointClick actions.\"},{\"kind\":\"crossFilter\",\"resolver\":\"x-ui-chart-events-cross-filter\",\"description\":\"Cross-filter event mapping emitted as structured query-context filters.\"},{\"kind\":\"drilldown\",\"resolver\":\"x-ui-chart-events-drill-down\",\"description\":\"Drilldown event mapping and governed target.\"},{\"kind\":\"selection\",\"resolver\":\"x-ui-chart-events-selection-change\",\"description\":\"Single-point selection event enablement and mapping semantics; toggle and multi-select are intentionally not advertised.\"},{\"kind\":\"legend\",\"resolver\":\"x-ui-chart-legend-feature\",\"description\":\"Legend visibility through boolean or toggleable feature object.\"},{\"kind\":\"tooltip\",\"resolver\":\"x-ui-chart-tooltip-feature\",\"description\":\"Tooltip visibility through boolean or toggleable feature object.\"}]}",
|
|
2376
2591
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
@@ -2380,7 +2595,7 @@
|
|
|
2380
2595
|
"corpusVersion": "1.0.0"
|
|
2381
2596
|
},
|
|
2382
2597
|
{
|
|
2383
|
-
"chunkIndex":
|
|
2598
|
+
"chunkIndex": 17,
|
|
2384
2599
|
"chunkKind": "authoring_manifest",
|
|
2385
2600
|
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"chart.document.set\",\"title\":\"Set chart document\",\"scope\":\"global\",\"targetKind\":\"dataBinding\",\"target\":{\"kind\":\"dataBinding\",\"resolver\":\"x-ui-chart-document-root\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"version\",\"kind\",\"source\"],\"properties\":{\"version\":{\"const\":\"0.1.0\"},\"kind\":{\"enum\":[\"bar\",\"combo\",\"horizontal-bar\",\"line\",\"pie\",\"donut\",\"area\",\"stacked-bar\",\"stacked-area\",\"scatter\"]},\"chartId\":{\"type\":\"string\"},\"title\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"object\"}]},\"subtitle\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"object\"}]},\"source\":{\"type\":\"object\",\"required\":[\"kind\"],\"properties\":{\"kind\":{\"enum\":[\"praxis.stats\",\"derived\"]},\"resource\":{\"type\":\"string\"},\"operation\":{\"enum\":[\"group-by\",\"timeseries\",\"distribution\",\"comparison\"]},\"options\":{\"type\":\"object\",\"properties\":{\"comparisonPeriod\":{\"type\":\"object\",\"required\":[\"field\",\"timezone\",\"preset\",\"mode\"],\"properties\":{\"field\":{\"type\":\"string\"},\"timezone\":{\"type\":\"string\"},\"preset\":{\"enum\":[\"TODAY\",\"YESTERDAY\",\"LAST_7_DAYS\",\"LAST_30_DAYS\",\"THIS_MONTH\",\"LAST_MONTH\",\"THIS_QUARTER\",\"THIS_YEAR\"]},\"mode\":{\"enum\":[\"PREVIOUS_ALIGNED\",\"PREVIOUS_CALENDAR_PERIOD\"]}}}}}}},\"dimensions\":{\"type\":\"array\",\"items\":{\"type\":\"object\"}},\"metrics\":{\"type\":\"array\",\"items\":{\"type\":\"object\"}},\"events\":{\"type\":\"object\"}}},\"effects\":[{\"kind\":\"set-value\",\"path\":\"chartDocument\"}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"chart-document-shape\",\"chart-version-supported\",\"chart-type-series-axis-compatible\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"chart.type.set\",\"title\":\"Set chart type\",\"scope\":\"global\",\"targetKind\":\"chartType\",\"target\":{\"kind\":\"chartType\",\"resolver\":\"x-ui-chart-kind\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"kind\"],\"properties\":{\"kind\":{\"enum\":[\"bar\",\"combo\",\"horizontal-bar\",\"line\",\"pie\",\"donut\",\"area\",\"stacked-bar\",\"stacked-area\",\"scatter\"]},\"orientation\":{\"enum\":[\"vertical\",\"horizontal\"]}}},\"effects\":[{\"kind\":\"merge-object\",\"path\":\"chartDocument\"}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"chart-type-supported\",\"chart-type-series-axis-compatible\",\"pie-single-metric\",\"combo-minimum-series\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.kind\",\"chartDocument.orientation\",\"chartDocument.metrics[]\",\"chartDocument.dimensions[]\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"series.add\",\"title\":\"Add chart series\",\"scope\":\"dataBinding\",\"targetKind\":\"series\",\"target\":{\"kind\":\"series\",\"resolver\":\"x-ui-chart-metric-by-field\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"field\"],\"properties\":{\"field\":{\"type\":\"string\"},\"label\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"object\"}]},\"aggregation\":{\"enum\":[\"sum\",\"avg\",\"min\",\"max\",\"count\",\"distinct-count\"]},\"seriesKind\":{\"enum\":[\"bar\",\"line\",\"area\"]},\"axis\":{\"enum\":[\"primary\",\"secondary\"]},\"color\":{\"type\":\"string\"},\"format\":{\"type\":\"string\"},\"afterField\":{\"type\":\"string\"}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-series-add\",\"handlerContract\":{\"reads\":[\"chartDocument.kind\",\"chartDocument.metrics[]\",\"availableFields[]\"],\"writes\":[\"chartDocument.metrics[]\"],\"identityKeys\":[\"chartDocument.metrics[].field\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"field\"],\"properties\":{\"field\":{\"type\":\"string\"},\"label\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"object\"}]},\"aggregation\":{\"enum\":[\"sum\",\"avg\",\"min\",\"max\",\"count\",\"distinct-count\"]},\"seriesKind\":{\"enum\":[\"bar\",\"line\",\"area\"]},\"axis\":{\"enum\":[\"primary\",\"secondary\"]},\"color\":{\"type\":\"string\"},\"format\":{\"type\":\"string\"},\"afterField\":{\"type\":\"string\"}}},\"failureModes\":[\"field-not-in-catalog\",\"duplicate-series-field\",\"aggregation-not-supported\",\"chart-type-series-incompatible\"],\"description\":\"Adds a metric-backed series by semantic field, validating it against availableFields and chart kind compatibility before patching metrics[].\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"series-field-exists\",\"series-field-aggregable\",\"series-id-unique\",\"chart-type-series-axis-compatible\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.metrics[]\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"series.remove\",\"title\":\"Remove chart series\",\"scope\":\"dataBinding\",\"targetKind\":\"series\",\"target\":{\"kind\":\"series\",\"resolver\":\"x-ui-chart-metric-by-field\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"required\":[\"field\"],\"properties\":{\"field\":{\"type\":\"string\"},\"replacementField\":{\"type\":\"string\"}}},\"effects\":[{\"kind\":\"remove-by-key\",\"path\":\"chartDocument.metrics[]\",\"key\":\"field\"}],\"destructive\":true,\"requiresConfirmation\":true,\"validators\":[\"series-exists\",\"destructive-series-removal-confirmed\",\"chart-keeps-required-metric\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.metrics[]\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"config-initialized\",\"target-series-exists\",\"confirmation-collected\"]},{\"operationId\":\"axis.configure\",\"title\":\"Configure chart axis\",\"scope\":\"dataBinding\",\"targetKind\":\"axis\",\"target\":{\"kind\":\"axis\",\"resolver\":\"x-ui-chart-dimension-or-metric-axis\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"properties\":{\"dimensionField\":{\"type\":\"string\"},\"dimensionRole\":{\"enum\":[\"category\",\"series\",\"segment\",\"time\",\"value\"]},\"metricField\":{\"type\":\"string\"},\"metricAxis\":{\"enum\":[\"primary\",\"secondary\"]},\"orientation\":{\"enum\":[\"vertical\",\"horizontal\"]}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-axis-configure\",\"handlerContract\":{\"reads\":[\"chartDocument.kind\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\",\"availableFields[]\"],\"writes\":[\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\",\"chartDocument.orientation\"],\"identityKeys\":[\"chartDocument.dimensions[].field\",\"chartDocument.metrics[].field\"],\"inputSchema\":{\"type\":\"object\",\"properties\":{\"dimensionField\":{\"type\":\"string\"},\"dimensionRole\":{\"enum\":[\"category\",\"series\",\"segment\",\"time\",\"value\"]},\"metricField\":{\"type\":\"string\"},\"metricAxis\":{\"enum\":[\"primary\",\"secondary\"]},\"orientation\":{\"enum\":[\"vertical\",\"horizontal\"]}}},\"failureModes\":[\"field-not-in-catalog\",\"secondary-axis-non-combo\",\"missing-required-dimension\",\"axis-role-incompatible\"],\"description\":\"Configures dimensions and metric axes using semantic fields, preserving combo-only secondary axis constraints.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"axis-field-exists\",\"secondary-axis-combo-only\",\"cartesian-dimension-required\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.dimensions[]\",\"chartDocument.metrics[].axis\",\"chartDocument.orientation\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"config-initialized\"]}]}",
|
|
2386
2601
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
@@ -2390,17 +2605,17 @@
|
|
|
2390
2605
|
"corpusVersion": "1.0.0"
|
|
2391
2606
|
},
|
|
2392
2607
|
{
|
|
2393
|
-
"chunkIndex":
|
|
2608
|
+
"chunkIndex": 18,
|
|
2394
2609
|
"chunkKind": "authoring_manifest",
|
|
2395
|
-
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"data.resource.bind\",\"title\":\"Bind chart data resource\",\"scope\":\"dataBinding\",\"targetKind\":\"dataBinding\",\"target\":{\"kind\":\"dataBinding\",\"resolver\":\"x-ui-chart-source-and-field-catalog\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"sourceKind\"],\"properties\":{\"sourceKind\":{\"enum\":[\"praxis.stats\",\"derived\"]},\"resource\":{\"type\":\"string\",\"pattern\":\"^/(?!/)\"},\"operation\":{},\"dimensions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"required\":[\"field\"],\"properties\":{\"field\":{\"type\":\"string\"},\"role\":{\"type\":\"string\"}}}},\"metrics\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"required\":[\"field\"],\"properties\":{\"field\":{\"type\":\"string\"},\"label\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"object\"}]},\"aggregation\":{\"enum\":[\"sum\",\"avg\",\"min\",\"max\",\"count\",\"distinct-count\"]},\"seriesKind\":{\"enum\":[\"bar\",\"line\",\"area\"]},\"axis\":{\"enum\":[\"primary\",\"secondary\"]},\"color\":{\"type\":\"string\"},\"format\":{\"type\":\"string\"},\"afterField\":{\"type\":\"string\"}}}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-data-resource-bind\",\"handlerContract\":{\"reads\":[\"availableResources[]\",\"availableFields[]\",\"chartDocument.source\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\"],\"writes\":[\"chartDocument.source\",\"chartDocument.source.options.comparisonPeriod\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\"],\"identityKeys\":[\"availableResources[].path\",\"availableFields[].field\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"sourceKind\"],\"properties\":{\"sourceKind\":{\"enum\":[\"praxis.stats\",\"derived\"]},\"resource\":{\"type\":\"string\",\"pattern\":\"^/(?!/)\"},\"operation\":{},\"dimensions\":{\"type\":\"array\"},\"metrics\":{\"type\":\"array\"}}},\"failureModes\":[\"resource-not-in-api-metadata\",\"field-not-in-schema\",\"stats-operation-not-supported\",\"comparison-period-incomplete\",\"comparison-metric-unsupported\",\"remote-binding-incomplete\"],\"description\":\"Binds a derived or praxis.stats source using governed resource, capability and field catalogs. For praxis.stats, availableResources[].id is discovery-only: validation requires the matching availableResources[].path and compilation persists that path. Comparison requires one canonical operation, metrics[] and an eligible period field.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"remote-resource-in-api-metadata\",\"bound-fields-exist\",\"stats-operation-supported\",\"comparison-period-complete\",\"comparison-metrics-supported\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.source\",\"chartDocument.source.options.comparisonPeriod\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\"],\"submissionImpact\":\"affects-remote-binding\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"queryContext.set\",\"title\":\"Set chart query context\",\"scope\":\"dataBinding\",\"targetKind\":\"queryContext\",\"target\":{\"kind\":\"queryContext\",\"resolver\":\"praxis-chart-query-context\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"properties\":{\"filters\":{\"type\":\"object\"},\"filterExpression\":{\"type\":\"object\"},\"sort\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"limit\":{\"type\":\"number\"},\"page\":{\"type\":\"object\"},\"meta\":{\"type\":\"object\"}}},\"effects\":[{\"kind\":\"set-value\",\"path\":\"queryContext\"}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"query-context-structured\",\"query-context-filter-expression-stats-unsupported\",\"query-context-fields-exist\",\"query-context-safe-values\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"queryContext\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"pointClick.configure\",\"title\":\"Configure chart point click\",\"scope\":\"eventMapping\",\"targetKind\":\"pointClick\",\"target\":{\"kind\":\"pointClick\",\"resolver\":\"x-ui-chart-events-point-click\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-event-point-click-configure\",\"handlerContract\":{\"reads\":[\"chartDocument.events.pointClick\",\"chartDocument.dimensions[]\",\"availableTargets[]\"],\"writes\":[\"chartDocument.events.pointClick\"],\"identityKeys\":[\"availableTargets[].id\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"failureModes\":[\"target-not-in-catalog\",\"mapping-source-field-missing\",\"mapping-target-invalid\",\"unsafe-event-action\"],\"description\":\"Configures pointClick through the pointAction output while keeping pointClick as raw point evidence.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"point-click-action-structured\",\"event-target-governed\",\"event-mapping-fields-exist\",\"event-action-supported\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.events.pointClick\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"crossFilter.configure\",\"title\":\"Configure chart cross-filter\",\"scope\":\"eventMapping\",\"targetKind\":\"crossFilter\",\"target\":{\"kind\":\"crossFilter\",\"resolver\":\"x-ui-chart-events-cross-filter\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-event-cross-filter-configure\",\"handlerContract\":{\"reads\":[\"chartDocument.events.crossFilter\",\"chartDocument.dimensions[]\",\"availableTargets[]\"],\"writes\":[\"chartDocument.events.crossFilter\"],\"identityKeys\":[\"availableTargets[].id\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"failureModes\":[\"target-not-in-catalog\",\"mapping-source-field-missing\",\"mapping-target-invalid\",\"unsafe-event-action\"],\"description\":\"Configures crossFilter as a structured event action emitted through the crossFilter output with safe query-context filters to a governed target. For praxis.stats rows, key is governed bucket identity and label is presentation evidence; mappings may use key when the target schema accepts that identity.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"cross-filter-output-structured\",\"event-target-governed\",\"event-mapping-fields-exist\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.events.crossFilter\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"config-initialized\"]}]}",
|
|
2610
|
+
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"data.resource.bind\",\"title\":\"Bind chart data resource\",\"scope\":\"dataBinding\",\"targetKind\":\"dataBinding\",\"target\":{\"kind\":\"dataBinding\",\"resolver\":\"x-ui-chart-source-and-field-catalog\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"sourceKind\"],\"properties\":{\"sourceKind\":{\"enum\":[\"praxis.stats\",\"derived\"]},\"resource\":{\"type\":\"string\",\"pattern\":\"^/(?!/)\"},\"operation\":{\"enum\":[\"group-by\",\"timeseries\",\"distribution\",\"comparison\"]},\"comparisonPeriod\":{\"type\":\"object\",\"required\":[\"field\",\"timezone\",\"preset\",\"mode\"],\"properties\":{\"field\":{\"type\":\"string\"},\"timezone\":{\"type\":\"string\"},\"preset\":{\"enum\":[\"TODAY\",\"YESTERDAY\",\"LAST_7_DAYS\",\"LAST_30_DAYS\",\"THIS_MONTH\",\"LAST_MONTH\",\"THIS_QUARTER\",\"THIS_YEAR\"]},\"mode\":{\"enum\":[\"PREVIOUS_ALIGNED\",\"PREVIOUS_CALENDAR_PERIOD\"]}}},\"dimensions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"required\":[\"field\"],\"properties\":{\"field\":{\"type\":\"string\"},\"role\":{\"type\":\"string\"}}}},\"metrics\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"required\":[\"field\"],\"properties\":{\"field\":{\"type\":\"string\"},\"label\":{\"oneOf\":[{\"type\":\"string\"},{\"type\":\"object\"}]},\"aggregation\":{\"enum\":[\"sum\",\"avg\",\"min\",\"max\",\"count\",\"distinct-count\"]},\"seriesKind\":{\"enum\":[\"bar\",\"line\",\"area\"]},\"axis\":{\"enum\":[\"primary\",\"secondary\"]},\"color\":{\"type\":\"string\"},\"format\":{\"type\":\"string\"},\"afterField\":{\"type\":\"string\"}}}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-data-resource-bind\",\"handlerContract\":{\"reads\":[\"availableResources[]\",\"availableFields[]\",\"chartDocument.source\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\"],\"writes\":[\"chartDocument.source\",\"chartDocument.source.options.comparisonPeriod\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\"],\"identityKeys\":[\"availableResources[].path\",\"availableFields[].field\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"sourceKind\"],\"properties\":{\"sourceKind\":{\"enum\":[\"praxis.stats\",\"derived\"]},\"resource\":{\"type\":\"string\",\"pattern\":\"^/(?!/)\"},\"operation\":{\"enum\":[\"group-by\",\"timeseries\",\"distribution\",\"comparison\"]},\"comparisonPeriod\":{\"type\":\"object\",\"required\":[\"field\",\"timezone\",\"preset\",\"mode\"],\"properties\":{\"field\":{\"type\":\"string\"},\"timezone\":{\"type\":\"string\"},\"preset\":{\"enum\":[\"TODAY\",\"YESTERDAY\",\"LAST_7_DAYS\",\"LAST_30_DAYS\",\"THIS_MONTH\",\"LAST_MONTH\",\"THIS_QUARTER\",\"THIS_YEAR\"]},\"mode\":{\"enum\":[\"PREVIOUS_ALIGNED\",\"PREVIOUS_CALENDAR_PERIOD\"]}}},\"dimensions\":{\"type\":\"array\"},\"metrics\":{\"type\":\"array\"}}},\"failureModes\":[\"resource-not-in-api-metadata\",\"field-not-in-schema\",\"stats-operation-not-supported\",\"comparison-period-incomplete\",\"comparison-metric-unsupported\",\"remote-binding-incomplete\"],\"description\":\"Binds a derived or praxis.stats source using governed resource, capability and field catalogs. For praxis.stats, availableResources[].id is discovery-only: validation requires the matching availableResources[].path and compilation persists that path. Comparison requires one canonical operation, metrics[] and an eligible period field.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"remote-resource-in-api-metadata\",\"bound-fields-exist\",\"stats-operation-supported\",\"comparison-period-complete\",\"comparison-metrics-supported\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.source\",\"chartDocument.source.options.comparisonPeriod\",\"chartDocument.dimensions[]\",\"chartDocument.metrics[]\"],\"submissionImpact\":\"affects-remote-binding\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"queryContext.set\",\"title\":\"Set chart query context\",\"scope\":\"dataBinding\",\"targetKind\":\"queryContext\",\"target\":{\"kind\":\"queryContext\",\"resolver\":\"praxis-chart-query-context\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"properties\":{\"filters\":{\"type\":\"object\"},\"filterExpression\":{\"type\":\"object\"},\"sort\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"limit\":{\"type\":\"number\"},\"page\":{\"type\":\"object\"},\"meta\":{\"type\":\"object\"}}},\"effects\":[{\"kind\":\"set-value\",\"path\":\"queryContext\"}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"query-context-structured\",\"query-context-filter-expression-stats-unsupported\",\"query-context-fields-exist\",\"query-context-safe-values\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"queryContext\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"pointClick.configure\",\"title\":\"Configure chart point click\",\"scope\":\"eventMapping\",\"targetKind\":\"pointClick\",\"target\":{\"kind\":\"pointClick\",\"resolver\":\"x-ui-chart-events-point-click\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-event-point-click-configure\",\"handlerContract\":{\"reads\":[\"chartDocument.events.pointClick\",\"chartDocument.dimensions[]\",\"availableTargets[]\"],\"writes\":[\"chartDocument.events.pointClick\"],\"identityKeys\":[\"availableTargets[].id\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"failureModes\":[\"target-not-in-catalog\",\"mapping-source-field-missing\",\"mapping-target-invalid\",\"unsafe-event-action\"],\"description\":\"Configures pointClick through the pointAction output while keeping pointClick as raw point evidence.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"point-click-action-structured\",\"event-target-governed\",\"event-mapping-fields-exist\",\"event-action-supported\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.events.pointClick\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"crossFilter.configure\",\"title\":\"Configure chart cross-filter\",\"scope\":\"eventMapping\",\"targetKind\":\"crossFilter\",\"target\":{\"kind\":\"crossFilter\",\"resolver\":\"x-ui-chart-events-cross-filter\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-event-cross-filter-configure\",\"handlerContract\":{\"reads\":[\"chartDocument.events.crossFilter\",\"chartDocument.dimensions[]\",\"availableTargets[]\"],\"writes\":[\"chartDocument.events.crossFilter\"],\"identityKeys\":[\"availableTargets[].id\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"failureModes\":[\"target-not-in-catalog\",\"mapping-source-field-missing\",\"mapping-target-invalid\",\"unsafe-event-action\"],\"description\":\"Configures crossFilter as a structured event action emitted through the crossFilter output with safe query-context filters to a governed target. For praxis.stats rows, key is governed bucket identity and label is presentation evidence; mappings may use key when the target schema accepts that identity.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"cross-filter-output-structured\",\"event-target-governed\",\"event-mapping-fields-exist\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.events.crossFilter\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"config-initialized\"]}]}",
|
|
2396
2611
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2397
|
-
"contentHash": "
|
|
2612
|
+
"contentHash": "c9f56ab0807b6aa3980f1e20ed30d34e0863dcf1761e9be82bf7c9c2e2e6dc29",
|
|
2398
2613
|
"sourceKind": "component_definition",
|
|
2399
2614
|
"sourceId": "praxis-chart",
|
|
2400
2615
|
"corpusVersion": "1.0.0"
|
|
2401
2616
|
},
|
|
2402
2617
|
{
|
|
2403
|
-
"chunkIndex":
|
|
2618
|
+
"chunkIndex": 19,
|
|
2404
2619
|
"chunkKind": "authoring_manifest",
|
|
2405
2620
|
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"drilldown.configure\",\"title\":\"Configure chart drilldown\",\"scope\":\"eventMapping\",\"targetKind\":\"drilldown\",\"target\":{\"kind\":\"drilldown\",\"resolver\":\"x-ui-chart-events-drill-down\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-event-drilldown-configure\",\"handlerContract\":{\"reads\":[\"chartDocument.events.drillDown\",\"chartDocument.dimensions[]\",\"availableTargets[]\"],\"writes\":[\"chartDocument.events.drillDown\"],\"identityKeys\":[\"availableTargets[].id\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"failureModes\":[\"target-not-in-catalog\",\"mapping-source-field-missing\",\"drilldown-target-invalid\",\"unsafe-event-action\"],\"description\":\"Configures drillDown through the dedicated drillDown output with governed route/widget targets and structured field mappings.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"drilldown-target-governed\",\"event-mapping-fields-exist\",\"event-action-supported\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.events.drillDown\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"selection.configure\",\"title\":\"Configure chart selection\",\"scope\":\"selection\",\"targetKind\":\"selection\",\"target\":{\"kind\":\"selection\",\"resolver\":\"x-ui-chart-events-selection-change\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"chart-event-selection-configure\",\"handlerContract\":{\"reads\":[\"chartDocument.events.selectionChange\",\"chartDocument.dimensions[]\",\"availableTargets[]\"],\"writes\":[\"chartDocument.events.selectionChange\"],\"identityKeys\":[\"availableTargets[].id\"],\"inputSchema\":{\"type\":\"object\",\"required\":[\"action\"],\"properties\":{\"action\":{\"enum\":[\"filter-widget\",\"open-detail\",\"navigate\",\"update-context\",\"emit\"]},\"target\":{\"type\":\"string\"},\"mapping\":{\"type\":\"object\",\"additionalProperties\":{\"type\":\"string\"}}}},\"failureModes\":[\"target-not-in-catalog\",\"mapping-source-field-missing\",\"mapping-target-invalid\",\"unsafe-event-action\"],\"description\":\"Configures selectionChange as single-point selection evidence whose event key is governed by the operation.\"}}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"selection-output-structured\",\"event-target-governed\",\"event-mapping-fields-exist\",\"event-action-supported\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.events.selectionChange\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"legend.configure\",\"title\":\"Configure chart legend\",\"scope\":\"skin\",\"targetKind\":\"legend\",\"target\":{\"kind\":\"legend\",\"resolver\":\"x-ui-chart-legend-feature\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"enabled\"],\"properties\":{\"enabled\":{\"type\":\"boolean\"}}},\"effects\":[{\"kind\":\"set-value\",\"path\":\"chartDocument.legend\"}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"feature-toggle-valid\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.legend\"],\"submissionImpact\":\"visual-only\",\"preconditions\":[\"config-initialized\"]},{\"operationId\":\"tooltip.configure\",\"title\":\"Configure chart tooltip\",\"scope\":\"skin\",\"targetKind\":\"tooltip\",\"target\":{\"kind\":\"tooltip\",\"resolver\":\"x-ui-chart-tooltip-feature\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"enabled\"],\"properties\":{\"enabled\":{\"type\":\"boolean\"}}},\"effects\":[{\"kind\":\"set-value\",\"path\":\"chartDocument.tooltip\"}],\"destructive\":false,\"requiresConfirmation\":false,\"validators\":[\"feature-toggle-valid\",\"editor-runtime-round-trip\"],\"affectedPaths\":[\"chartDocument.tooltip\"],\"submissionImpact\":\"visual-only\",\"preconditions\":[\"config-initialized\"]}]}",
|
|
2406
2621
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
@@ -2410,7 +2625,7 @@
|
|
|
2410
2625
|
"corpusVersion": "1.0.0"
|
|
2411
2626
|
},
|
|
2412
2627
|
{
|
|
2413
|
-
"chunkIndex":
|
|
2628
|
+
"chunkIndex": 20,
|
|
2414
2629
|
"chunkKind": "authoring_manifest",
|
|
2415
2630
|
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"validators\",\"validators\":[{\"validatorId\":\"chart-document-shape\",\"level\":\"error\",\"code\":\"CHART_DOCUMENT_SHAPE\",\"description\":\"Document must conform to PraxisXUiChartContract.\"},{\"validatorId\":\"chart-version-supported\",\"level\":\"error\",\"code\":\"CHART_VERSION_SUPPORTED\"},{\"validatorId\":\"chart-type-supported\",\"level\":\"error\",\"code\":\"CHART_TYPE_SUPPORTED\",\"description\":\"Chart kind must be supported by @praxisui/charts.\"},{\"validatorId\":\"chart-type-series-axis-compatible\",\"level\":\"error\",\"code\":\"CHART_TYPE_SERIES_AXIS_COMPATIBLE\",\"description\":\"Chart kind, dimensions, metrics and axes must be compatible.\"},{\"validatorId\":\"pie-single-metric\",\"level\":\"error\",\"code\":\"PIE_SINGLE_METRIC\",\"description\":\"Pie and donut charts support a single metric.\"},{\"validatorId\":\"combo-minimum-series\",\"level\":\"error\",\"code\":\"COMBO_MINIMUM_SERIES\",\"description\":\"Combo charts require at least two metrics.\"},{\"validatorId\":\"series-field-exists\",\"level\":\"error\",\"code\":\"SERIES_FIELD_EXISTS\",\"description\":\"Series field must exist in availableFields or schema context.\"},{\"validatorId\":\"series-field-aggregable\",\"level\":\"error\",\"code\":\"SERIES_FIELD_AGGREGABLE\",\"description\":\"Metric fields must support the requested aggregation.\"},{\"validatorId\":\"series-id-unique\",\"level\":\"error\",\"code\":\"SERIES_ID_UNIQUE\",\"description\":\"Metric/series identity must be stable and unique by field.\"},{\"validatorId\":\"series-exists\",\"level\":\"error\",\"code\":\"SERIES_EXISTS\",\"description\":\"Series removal target must exist.\"},{\"validatorId\":\"destructive-series-removal-confirmed\",\"level\":\"error\",\"code\":\"DESTRUCTIVE_SERIES_REMOVAL_CONFIRMED\",\"description\":\"Removing a series requires explicit confirmation.\"},{\"validatorId\":\"chart-keeps-required-metric\",\"level\":\"error\",\"code\":\"CHART_KEEPS_REQUIRED_METRIC\",\"description\":\"A chart cannot be left without required metrics.\"},{\"validatorId\":\"axis-field-exists\",\"level\":\"error\",\"code\":\"AXIS_FIELD_EXISTS\",\"description\":\"Axis/dimension fields must exist in availableFields or schema context.\"},{\"validatorId\":\"secondary-axis-combo-only\",\"level\":\"error\",\"code\":\"SECONDARY_AXIS_COMBO_ONLY\",\"description\":\"Secondary axis is supported only for combo charts.\"},{\"validatorId\":\"cartesian-dimension-required\",\"level\":\"error\",\"code\":\"CARTESIAN_DIMENSION_REQUIRED\",\"description\":\"Cartesian charts require at least one dimension.\"},{\"validatorId\":\"remote-resource-in-api-metadata\",\"level\":\"error\",\"code\":\"REMOTE_RESOURCE_IN_API_METADATA\",\"description\":\"Remote praxis.stats resource must equal a governed root-relative availableResources[].path beginning with exactly one slash; a discovery id is not an executable resource path.\"},{\"validatorId\":\"bound-fields-exist\",\"level\":\"error\",\"code\":\"BOUND_FIELDS_EXIST\",\"description\":\"Bound dimensions and metrics must exist in the capability-backed field context and satisfy its operation, aggregation and distribution-mode eligibility.\"},{\"validatorId\":\"stats-operation-supported\",\"level\":\"error\",\"code\":\"STATS_OPERATION_SUPPORTED\",\"description\":\"Remote datasource operation must be supported by the stats backend.\"},{\"validatorId\":\"comparison-period-complete\",\"level\":\"error\",\"code\":\"COMPARISON_PERIOD_COMPLETE\",\"description\":\"Comparison requires a time-series eligible period field, timezone, preset and mode grounded in the governed resource capabilities.\"},{\"validatorId\":\"comparison-metrics-supported\",\"level\":\"error\",\"code\":\"COMPARISON_METRICS_SUPPORTED\",\"description\":\"Comparison requires unique metrics[] aliases using only COUNT, DISTINCT_COUNT or SUM capabilities; singular metric payloads are not authorable.\"},{\"validatorId\":\"query-context-structured\",\"level\":\"error\",\"code\":\"QUERY_CONTEXT_STRUCTURED\",\"description\":\"queryContext must use the canonical @praxisui/core structure for filters, filterExpression, sort, limit, page and meta.\"},{\"validatorId\":\"query-context-filter-expression-stats-unsupported\",\"level\":\"error\",\"code\":\"QUERY_CONTEXT_FILTER_EXPRESSION_STATS_UNSUPPORTED\",\"description\":\"praxis.stats currently executes only flat queryContext.filters; governed filterExpression must be preserved for diagnostics and rejected until backend expression support is declared.\"},{\"validatorId\":\"query-context-fields-exist\",\"level\":\"warning\",\"code\":\"QUERY_CONTEXT_FIELDS_EXIST\",\"description\":\"queryContext filter fields should exist in the schema/data context.\"},{\"validatorId\":\"query-context-safe-values\",\"level\":\"error\",\"code\":\"QUERY_CONTEXT_SAFE_VALUES\",\"description\":\"queryContext values must be serializable safe data.\"},{\"validatorId\":\"point-click-action-structured\",\"level\":\"error\",\"code\":\"POINT_CLICK_ACTION_STRUCTURED\",\"description\":\"Configured pointClick actions must be emitted through pointAction while pointClick remains raw point evidence.\"},{\"validatorId\":\"cross-filter-output-structured\",\"level\":\"error\",\"code\":\"CROSS_FILTER_OUTPUT_STRUCTURED\",\"description\":\"Cross-filter output must be structured as query-context filters.\"},{\"validatorId\":\"event-target-governed\",\"level\":\"error\",\"code\":\"EVENT_TARGET_GOVERNED\",\"description\":\"Event targets must resolve to exactly one availableTargets[].id and declare the authored event plus action; missing, duplicate or cross-event identities fail closed.\"},{\"validatorId\":\"event-mapping-fields-exist\",\"level\":\"error\",\"code\":\"EVENT_MAPPING_FIELDS_EXIST\",\"description\":\"Event mapping source fields must exist in chart data context; praxis.stats reserves key for bucket identity and label for presentation.\"},{\"validatorId\":\"event-action-supported\",\"level\":\"error\",\"code\":\"EVENT_ACTION_SUPPORTED\",\"description\":\"Event action must be one of the supported structured action kinds.\"},{\"validatorId\":\"drilldown-target-governed\",\"level\":\"error\",\"code\":\"DRILLDOWN_TARGET_GOVERNED\",\"description\":\"Drilldown target must be a governed route or widget target.\"},{\"validatorId\":\"selection-output-structured\",\"level\":\"error\",\"code\":\"SELECTION_OUTPUT_STRUCTURED\",\"description\":\"Selection output must resolve selected point filters deterministically.\"},{\"validatorId\":\"feature-toggle-valid\",\"level\":\"error\",\"code\":\"FEATURE_TOGGLE_VALID\",\"description\":\"Legend and tooltip feature values must be boolean or toggleable feature objects.\"},{\"validatorId\":\"editor-runtime-round-trip\",\"level\":\"error\",\"code\":\"EDITOR_RUNTIME_ROUND_TRIP\",\"description\":\"PraxisChartConfigEditor must save/reopen the same canonical document that runtime consumes.\"}]}",
|
|
2416
2631
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
@@ -2420,7 +2635,7 @@
|
|
|
2420
2635
|
"corpusVersion": "1.0.0"
|
|
2421
2636
|
},
|
|
2422
2637
|
{
|
|
2423
|
-
"chunkIndex":
|
|
2638
|
+
"chunkIndex": 21,
|
|
2424
2639
|
"chunkKind": "authoring_manifest",
|
|
2425
2640
|
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"roundTripRequirements\",\"roundTripRequirements\":[\"The canonical saved shape is PraxisXUiChartContract, not raw ECharts options.\",\"PraxisChartConfigEditor must preserve chart kind, source, dimensions, metrics, events, legend and tooltip across apply/save/reset/reopen.\",\"Remote bindings persist availableResources[].path; availableResources[].id remains discovery identity, and transient resource/field/target catalogs and diagnostics are never persisted in widget inputs.\",\"Comparison must preserve one canonical /stats/comparison request with metrics[] and a complete comparisonPeriod; it must not downgrade into independent group-by calls.\",\"Cross-filter, drilldown and selection authoring must persist structured event actions, not prompt examples or command strings.\",\"[P1] Backend handler, resolver and validators now execute directly; end-to-end Page Builder parity remains open until assistant requests project the transient availableTargets[].events catalog through validationContext and expose its fail-closed diagnostics.\",\"[P2] Transform-output semantics and target input-schema/port fields must be materialized and validated by Core; local transform.output inspection and backend-enriched inputFields are not substitutes for those canonical projections.\",\"Enrichment of capability-backed availableFields with /schemas/filtered property metadata remains a later schema-flow gate.\"]}",
|
|
2426
2641
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
@@ -2430,11 +2645,11 @@
|
|
|
2430
2645
|
"corpusVersion": "1.0.0"
|
|
2431
2646
|
},
|
|
2432
2647
|
{
|
|
2433
|
-
"chunkIndex":
|
|
2648
|
+
"chunkIndex": 22,
|
|
2434
2649
|
"chunkKind": "authoring_manifest",
|
|
2435
|
-
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"examples\",\"examples\":[{\"id\":\"set-bar-chart\",\"request\":\"Use a bar chart for employees by department.\",\"operationId\":\"chart.type.set\",\"params\":{\"kind\":\"bar\"},\"isPositive\":true},{\"id\":\"add-salary-series\",\"request\":\"Add salary as a summed metric.\",\"operationId\":\"series.add\",\"params\":{\"field\":\"salary\",\"aggregation\":\"sum\",\"label\":\"Salary\"},\"isPositive\":true},{\"id\":\"remove-old-series\",\"request\":\"Remove the old cost series.\",\"operationId\":\"series.remove\",\"target\":\"cost\",\"params\":{\"field\":\"cost\"},\"isPositive\":true},{\"id\":\"configure-secondary-axis\",\"request\":\"Put margin on a secondary axis for the combo chart.\",\"operationId\":\"axis.configure\",\"params\":{\"metricField\":\"margin\",\"metricAxis\":\"secondary\"},\"isPositive\":true},{\"id\":\"bind-stats-resource\",\"request\":\"Bind this chart to payroll stats grouped by department.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"/api/stats/payroll\",\"operation\":\"group-by\"},\"isPositive\":true},{\"id\":\"bind-comparison-resource\",\"request\":\"Compare active employees by department with the previous aligned 30-day period.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"/api/human-resources/employees\",\"operation\":\"comparison\",\"comparisonPeriod\":{\"field\":\"admissionDate\",\"timezone\":\"America/Sao_Paulo\",\"preset\":\"LAST_30_DAYS\",\"mode\":\"PREVIOUS_ALIGNED\"},\"dimensions\":[{\"field\":\"department\"}],\"metrics\":[{\"field\":\"employeeId\",\"aggregation\":\"distinct-count\"}]},\"isPositive\":true},{\"id\":\"set-query-context\",\"request\":\"Limit the chart to active employees and sort by total descending.\",\"operationId\":\"queryContext.set\",\"params\":{\"filters\":{\"status\":\"ACTIVE\"},\"sort\":[\"total,desc\"],\"limit\":10},\"isPositive\":true},{\"id\":\"configure-cross-filter\",\"request\":\"Use selected department to filter the employee table.\",\"operationId\":\"crossFilter.configure\",\"params\":{\"action\":\"filter-widget\",\"target\":\"employeesTable\",\"mapping\":{\"department\":\"department\"}},\"isPositive\":true},{\"id\":\"configure-stats-key-cross-filter\",\"request\":\"Use the governed department bucket identity to filter the employee table.\",\"operationId\":\"crossFilter.configure\",\"params\":{\"action\":\"filter-widget\",\"target\":\"employeesTable\",\"mapping\":{\"key\":\"departmentIdsIn\"}},\"isPositive\":true},{\"id\":\"configure-drilldown\",\"request\":\"Open the department detail when a point is clicked.\",\"operationId\":\"drilldown.configure\",\"params\":{\"action\":\"navigate\",\"target\":\"/departments/detail\",\"mapping\":{\"department\":\"departmentId\"}},\"isPositive\":true},{\"id\":\"configure-selection\",\"request\":\"Emit selected department as a structured selection event.\",\"operationId\":\"selection.configure\",\"params\":{\"action\":\"emit\",\"mapping\":{\"department\":\"department\"}},\"isPositive\":true},{\"id\":\"toggle-legend\",\"request\":\"Show the legend.\",\"operationId\":\"legend.configure\",\"params\":{\"enabled\":true},\"isPositive\":true},{\"id\":\"toggle-tooltip\",\"request\":\"Disable tooltips.\",\"operationId\":\"tooltip.configure\",\"params\":{\"enabled\":false},\"isPositive\":true},{\"id\":\"round-trip-editor-runtime\",\"request\":\"Save this chart in the editor and reopen it without changing the runtime chart document.\",\"operationId\":\"chart.document.set\",\"params\":{\"kind\":\"bar\",\"source\":{\"kind\":\"derived\"},\"dimensions\":[{\"field\":\"department\"}],\"metrics\":[{\"field\":\"total\",\"aggregation\":\"count\"}]},\"isPositive\":true},{\"id\":\"reject-ambiguous-series-target\",\"request\":\"Remove total when more than one chart series resolves to total.\",\"operationId\":\"series.remove\",\"target\":\"total\",\"params\":{\"field\":\"total\"},\"isPositive\":false},{\"id\":\"reject-prompt-routing\",\"request\":\"Use the prompt example text to decide which backend endpoint to call.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"from prompt example\"},\"isPositive\":false},{\"id\":\"reject-unsafe-target\",\"request\":\"Navigate to javascript:alert(1) on drilldown.\",\"operationId\":\"drilldown.configure\",\"params\":{\"event\":\"drillDown\",\"action\":\"navigate\",\"target\":\"javascript:alert(1)\"},\"isPositive\":false}]}",
|
|
2650
|
+
"content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.1.0\",\"componentId\":\"praxis-chart\",\"ownerPackage\":\"@praxisui/charts\",\"configSchemaId\":\"PraxisXUiChartContract\",\"chunkSection\":\"examples\",\"examples\":[{\"id\":\"set-bar-chart\",\"request\":\"Use a bar chart for employees by department.\",\"operationId\":\"chart.type.set\",\"params\":{\"kind\":\"bar\"},\"isPositive\":true},{\"id\":\"add-salary-series\",\"request\":\"Add salary as a summed metric.\",\"operationId\":\"series.add\",\"params\":{\"field\":\"salary\",\"aggregation\":\"sum\",\"label\":\"Salary\"},\"isPositive\":true},{\"id\":\"remove-old-series\",\"request\":\"Remove the old cost series.\",\"operationId\":\"series.remove\",\"target\":\"cost\",\"params\":{\"field\":\"cost\"},\"isPositive\":true},{\"id\":\"configure-secondary-axis\",\"request\":\"Put margin on a secondary axis for the combo chart.\",\"operationId\":\"axis.configure\",\"params\":{\"metricField\":\"margin\",\"metricAxis\":\"secondary\"},\"isPositive\":true},{\"id\":\"bind-stats-resource\",\"request\":\"Bind this chart to payroll stats grouped by department.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"/api/stats/payroll\",\"operation\":\"group-by\"},\"isPositive\":true},{\"id\":\"bind-comparison-resource\",\"request\":\"Compare active employees by department with the previous aligned 30-day period.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"/api/human-resources/employees\",\"operation\":\"comparison\",\"comparisonPeriod\":{\"field\":\"admissionDate\",\"timezone\":\"America/Sao_Paulo\",\"preset\":\"LAST_30_DAYS\",\"mode\":\"PREVIOUS_ALIGNED\"},\"dimensions\":[{\"field\":\"department\"}],\"metrics\":[{\"field\":\"employeeId\",\"aggregation\":\"distinct-count\"}]},\"isPositive\":true},{\"id\":\"set-query-context\",\"request\":\"Limit the chart to active employees and sort by total descending.\",\"operationId\":\"queryContext.set\",\"params\":{\"filters\":{\"status\":\"ACTIVE\"},\"sort\":[\"total,desc\"],\"limit\":10},\"isPositive\":true},{\"id\":\"configure-cross-filter\",\"request\":\"Use selected department to filter the employee table.\",\"operationId\":\"crossFilter.configure\",\"params\":{\"action\":\"filter-widget\",\"target\":\"employeesTable\",\"mapping\":{\"department\":\"department\"}},\"isPositive\":true},{\"id\":\"configure-stats-key-cross-filter\",\"request\":\"Use the governed department bucket identity to filter the employee table.\",\"operationId\":\"crossFilter.configure\",\"params\":{\"action\":\"filter-widget\",\"target\":\"employeesTable\",\"mapping\":{\"key\":\"departmentIdsIn\"}},\"isPositive\":true},{\"id\":\"configure-drilldown\",\"request\":\"Open the department detail when a point is clicked.\",\"operationId\":\"drilldown.configure\",\"params\":{\"action\":\"navigate\",\"target\":\"/departments/detail\",\"mapping\":{\"department\":\"departmentId\"}},\"isPositive\":true},{\"id\":\"configure-selection\",\"request\":\"Emit selected department as a structured selection event.\",\"operationId\":\"selection.configure\",\"params\":{\"action\":\"emit\",\"mapping\":{\"department\":\"department\"}},\"isPositive\":true},{\"id\":\"toggle-legend\",\"request\":\"Show the legend.\",\"operationId\":\"legend.configure\",\"params\":{\"enabled\":true},\"isPositive\":true},{\"id\":\"toggle-tooltip\",\"request\":\"Disable tooltips.\",\"operationId\":\"tooltip.configure\",\"params\":{\"enabled\":false},\"isPositive\":true},{\"id\":\"round-trip-editor-runtime\",\"request\":\"Save this chart in the editor and reopen it without changing the runtime chart document.\",\"operationId\":\"chart.document.set\",\"params\":{\"version\":\"0.1.0\",\"kind\":\"bar\",\"source\":{\"kind\":\"derived\"},\"dimensions\":[{\"field\":\"department\"}],\"metrics\":[{\"field\":\"total\",\"aggregation\":\"count\"}]},\"isPositive\":true},{\"id\":\"reject-ambiguous-series-target\",\"request\":\"Remove total when more than one chart series resolves to total.\",\"operationId\":\"series.remove\",\"target\":\"total\",\"params\":{\"field\":\"total\"},\"isPositive\":false},{\"id\":\"reject-prompt-routing\",\"request\":\"Use the prompt example text to decide which backend endpoint to call.\",\"operationId\":\"data.resource.bind\",\"params\":{\"sourceKind\":\"praxis.stats\",\"resource\":\"from prompt example\"},\"isPositive\":false},{\"id\":\"reject-unsafe-target\",\"request\":\"Navigate to javascript:alert(1) on drilldown.\",\"operationId\":\"drilldown.configure\",\"params\":{\"event\":\"drillDown\",\"action\":\"navigate\",\"target\":\"javascript:alert(1)\"},\"isPositive\":false}]}",
|
|
2436
2651
|
"sourcePointer": "projects/praxis-charts/src/lib/ai/praxis-charts-authoring-manifest.ts",
|
|
2437
|
-
"contentHash": "
|
|
2652
|
+
"contentHash": "afbff71974255aa301bda6746460ef1bd6e57cb562650cdd84fb1a7ae4c26b84",
|
|
2438
2653
|
"sourceKind": "component_definition",
|
|
2439
2654
|
"sourceId": "praxis-chart",
|
|
2440
2655
|
"corpusVersion": "1.0.0"
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, Optional, Inject, InjectionToken, input, booleanAttribute, output, viewChild, inject, ElementRef, NgZone, DestroyRef, signal, computed, afterNextRender, effect, ChangeDetectionStrategy, Component, ViewChild, Input, Injector, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import * as i1 from '@praxisui/core';
|
|
4
|
-
import { buildApiUrl, API_URL, PraxisI18nService, SETTINGS_PANEL_BRIDGE, normalizePraxisDataQueryContext, resolvePraxisFilterCriteria, BUILTIN_PAGE_THEME_PRESETS, normalizePraxisPresentationVisualization, providePraxisI18n, ComponentMetadataRegistry, ResourceDiscoveryService, SETTINGS_PANEL_DATA, createDefaultTableConfig, PRAXIS_TABLE_DETAIL_INLINE_RENDERERS, AnalyticsStatsRequestBuilderService, DynamicWidgetPageComponent } from '@praxisui/core';
|
|
4
|
+
import { buildApiUrl, API_URL, PraxisI18nService, SETTINGS_PANEL_BRIDGE, normalizePraxisDataQueryContext, resolvePraxisFilterCriteria, BUILTIN_PAGE_THEME_PRESETS, PraxisIconButtonComponent, normalizePraxisPresentationVisualization, providePraxisI18n, ComponentMetadataRegistry, ResourceDiscoveryService, SETTINGS_PANEL_DATA, createDefaultTableConfig, PRAXIS_TABLE_DETAIL_INLINE_RENDERERS, AnalyticsStatsRequestBuilderService, DynamicWidgetPageComponent } from '@praxisui/core';
|
|
5
5
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
6
|
import { throwError, map, of, isObservable, from, firstValueFrom, timeout, BehaviorSubject, Subscription } from 'rxjs';
|
|
7
|
-
import * as i1$2 from '@angular/material/
|
|
8
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
9
|
-
import * as i2 from '@angular/material/icon';
|
|
10
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
11
|
-
import * as i3 from '@angular/material/tooltip';
|
|
7
|
+
import * as i1$2 from '@angular/material/tooltip';
|
|
12
8
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
13
9
|
import * as i1$1 from '@angular/common/http';
|
|
14
10
|
import { HttpErrorResponse } from '@angular/common/http';
|
|
@@ -19,7 +15,9 @@ import { AriaComponent, DatasetComponent, GridComponent, LegendComponent, TitleC
|
|
|
19
15
|
import { CanvasRenderer } from 'echarts/renderers';
|
|
20
16
|
import * as i1$3 from '@angular/forms';
|
|
21
17
|
import { FormsModule } from '@angular/forms';
|
|
22
|
-
import * as
|
|
18
|
+
import * as i2 from '@angular/material/button';
|
|
19
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
20
|
+
import * as i3 from '@angular/material/card';
|
|
23
21
|
import { MatCardModule } from '@angular/material/card';
|
|
24
22
|
import * as i4 from '@angular/material/form-field';
|
|
25
23
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
@@ -2442,14 +2440,14 @@ class PraxisChartComponent {
|
|
|
2442
2440
|
@if (canOpenConfigEditor()) {
|
|
2443
2441
|
<button
|
|
2444
2442
|
class="praxis-chart-settings-trigger"
|
|
2445
|
-
|
|
2443
|
+
praxisIconButton="tune"
|
|
2444
|
+
size="compact"
|
|
2445
|
+
appearance="filled"
|
|
2446
2446
|
type="button"
|
|
2447
2447
|
[attr.aria-label]="editChartLabel()"
|
|
2448
2448
|
[matTooltip]="editChartLabel()"
|
|
2449
2449
|
(click)="openConfigEditor()"
|
|
2450
|
-
>
|
|
2451
|
-
<mat-icon>tune</mat-icon>
|
|
2452
|
-
</button>
|
|
2450
|
+
></button>
|
|
2453
2451
|
}
|
|
2454
2452
|
|
|
2455
2453
|
@if (loadState() === 'loading') {
|
|
@@ -2498,13 +2496,13 @@ class PraxisChartComponent {
|
|
|
2498
2496
|
<div #chartHost class="praxis-chart-host"></div>
|
|
2499
2497
|
}
|
|
2500
2498
|
</section>
|
|
2501
|
-
`, isInline: true, styles: [":host{display:block;min-height:var(--praxis-chart-runtime-height, 320px);min-width:0;color:var(--md-sys-color-on-surface, #1a1b20)}:host(.praxis-chart-host-fill-container){height:100%;min-height:0}:host-context(.pdx-shell.no-shell) .praxis-chart-shell,:host-context(.pdx-shell.body-fill) .praxis-chart-shell,:host-context(.pdx-shell.expanded) .praxis-chart-shell,:host-context(.pdx-shell.fullscreen) .praxis-chart-shell{height:100%!important}.praxis-chart-shell{position:relative;width:100%;height:100%;min-height:240px;border-radius:var(--praxis-chart-config-surface-radius, var(--praxis-chart-surface-radius, 8px));overflow:hidden;background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-surface-bg, var(--md-sys-color-surface-container-lowest, #fff)) );border-color:var( --praxis-chart-config-surface-border, var( --praxis-chart-surface-border, color-mix(in srgb, var(--md-sys-color-outline, #c5c7ce) 44%, transparent) ) );border-style:solid;border-width:var(--praxis-chart-config-surface-border-width, 1px)}.praxis-chart-shell-fill-container{min-height:0}:host-context(.pdx-shell) .praxis-chart-shell:not(.praxis-chart-shell-contained),.praxis-chart-shell-embedded{border-width:var( --praxis-chart-config-surface-border-width, var(--praxis-chart-embedded-surface-border-width, 0) );border-radius:var( --praxis-chart-config-surface-radius, var(--praxis-chart-embedded-surface-radius, 0) );background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-embedded-surface-bg, transparent) )}.praxis-chart-settings-trigger{position:absolute;top:10px;right:10px;z-index:3
|
|
2499
|
+
`, isInline: true, styles: [":host{display:block;min-height:var(--praxis-chart-runtime-height, 320px);min-width:0;color:var(--md-sys-color-on-surface, #1a1b20)}:host(.praxis-chart-host-fill-container){height:100%;min-height:0}:host-context(.pdx-shell.no-shell) .praxis-chart-shell,:host-context(.pdx-shell.body-fill) .praxis-chart-shell,:host-context(.pdx-shell.expanded) .praxis-chart-shell,:host-context(.pdx-shell.fullscreen) .praxis-chart-shell{height:100%!important}.praxis-chart-shell{position:relative;width:100%;height:100%;min-height:240px;border-radius:var(--praxis-chart-config-surface-radius, var(--praxis-chart-surface-radius, 8px));overflow:hidden;background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-surface-bg, var(--md-sys-color-surface-container-lowest, #fff)) );border-color:var( --praxis-chart-config-surface-border, var( --praxis-chart-surface-border, color-mix(in srgb, var(--md-sys-color-outline, #c5c7ce) 44%, transparent) ) );border-style:solid;border-width:var(--praxis-chart-config-surface-border-width, 1px)}.praxis-chart-shell-fill-container{min-height:0}:host-context(.pdx-shell) .praxis-chart-shell:not(.praxis-chart-shell-contained),.praxis-chart-shell-embedded{border-width:var( --praxis-chart-config-surface-border-width, var(--praxis-chart-embedded-surface-border-width, 0) );border-radius:var( --praxis-chart-config-surface-radius, var(--praxis-chart-embedded-surface-radius, 0) );background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-embedded-surface-bg, transparent) )}.praxis-chart-settings-trigger{position:absolute;top:10px;right:10px;z-index:3;--praxis-icon-button-filled-background: color-mix(in srgb, var(--md-sys-color-surface, #fff) 88%, rgba(18, 99, 180, .12));--praxis-icon-button-filled-foreground: var(--md-sys-color-on-surface-variant, #44474f);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.praxis-chart-host{width:100%;height:100%;min-height:inherit}.praxis-chart-state{height:100%;min-height:min(240px,100%);display:grid;align-content:center;justify-items:center;gap:14px;padding:24px;text-align:center}.praxis-chart-state-copy{display:grid;gap:6px;justify-items:center}.praxis-chart-state-title{font-size:1rem;font-weight:600;color:var(--md-sys-color-on-surface, #1a1b20)}.praxis-chart-state-description{font-size:.925rem;color:var(--md-sys-color-on-surface-variant, #5a5d67);max-width:36rem}.praxis-chart-loading-hero{width:min(100%,320px);display:grid;gap:14px}.praxis-chart-loading-summary{display:flex;gap:8px;justify-content:center}.praxis-chart-loading-chip,.praxis-chart-loading-bar{border-radius:999px;background:linear-gradient(90deg,#1263b414,#1263b438,#1263b414);background-size:200% 100%;animation:praxis-chart-loading-wave 1.2s ease-in-out infinite}.praxis-chart-loading-chip{display:block;width:104px;height:12px}.praxis-chart-loading-chip--short{width:64px}.praxis-chart-loading-plot{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));align-items:end;gap:10px;height:108px;padding:12px 8px 4px;border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container, #eef3f8) 72%,transparent);border:1px solid rgba(18,99,180,.08)}.praxis-chart-loading-bar{display:block;width:100%}.praxis-chart-loading-bar--1{height:32%}.praxis-chart-loading-bar--2{height:68%}.praxis-chart-loading-bar--3{height:48%}.praxis-chart-loading-bar--4{height:78%}.praxis-chart-loading-bar--5{height:56%}@keyframes praxis-chart-loading-wave{0%{background-position:100% 0}to{background-position:-100% 0}}\n"], dependencies: [{ kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2502
2500
|
}
|
|
2503
2501
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisChartComponent, decorators: [{
|
|
2504
2502
|
type: Component,
|
|
2505
2503
|
args: [{ selector: 'praxis-chart', standalone: true, host: {
|
|
2506
2504
|
'[class.praxis-chart-host-fill-container]': 'isFillContainerMode()',
|
|
2507
|
-
}, imports: [
|
|
2505
|
+
}, imports: [MatTooltipModule, PraxisIconButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2508
2506
|
<section
|
|
2509
2507
|
class="praxis-chart-shell"
|
|
2510
2508
|
[class.praxis-chart-shell-fill-container]="isFillContainerMode()"
|
|
@@ -2522,14 +2520,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
2522
2520
|
@if (canOpenConfigEditor()) {
|
|
2523
2521
|
<button
|
|
2524
2522
|
class="praxis-chart-settings-trigger"
|
|
2525
|
-
|
|
2523
|
+
praxisIconButton="tune"
|
|
2524
|
+
size="compact"
|
|
2525
|
+
appearance="filled"
|
|
2526
2526
|
type="button"
|
|
2527
2527
|
[attr.aria-label]="editChartLabel()"
|
|
2528
2528
|
[matTooltip]="editChartLabel()"
|
|
2529
2529
|
(click)="openConfigEditor()"
|
|
2530
|
-
>
|
|
2531
|
-
<mat-icon>tune</mat-icon>
|
|
2532
|
-
</button>
|
|
2530
|
+
></button>
|
|
2533
2531
|
}
|
|
2534
2532
|
|
|
2535
2533
|
@if (loadState() === 'loading') {
|
|
@@ -2578,7 +2576,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
2578
2576
|
<div #chartHost class="praxis-chart-host"></div>
|
|
2579
2577
|
}
|
|
2580
2578
|
</section>
|
|
2581
|
-
`, styles: [":host{display:block;min-height:var(--praxis-chart-runtime-height, 320px);min-width:0;color:var(--md-sys-color-on-surface, #1a1b20)}:host(.praxis-chart-host-fill-container){height:100%;min-height:0}:host-context(.pdx-shell.no-shell) .praxis-chart-shell,:host-context(.pdx-shell.body-fill) .praxis-chart-shell,:host-context(.pdx-shell.expanded) .praxis-chart-shell,:host-context(.pdx-shell.fullscreen) .praxis-chart-shell{height:100%!important}.praxis-chart-shell{position:relative;width:100%;height:100%;min-height:240px;border-radius:var(--praxis-chart-config-surface-radius, var(--praxis-chart-surface-radius, 8px));overflow:hidden;background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-surface-bg, var(--md-sys-color-surface-container-lowest, #fff)) );border-color:var( --praxis-chart-config-surface-border, var( --praxis-chart-surface-border, color-mix(in srgb, var(--md-sys-color-outline, #c5c7ce) 44%, transparent) ) );border-style:solid;border-width:var(--praxis-chart-config-surface-border-width, 1px)}.praxis-chart-shell-fill-container{min-height:0}:host-context(.pdx-shell) .praxis-chart-shell:not(.praxis-chart-shell-contained),.praxis-chart-shell-embedded{border-width:var( --praxis-chart-config-surface-border-width, var(--praxis-chart-embedded-surface-border-width, 0) );border-radius:var( --praxis-chart-config-surface-radius, var(--praxis-chart-embedded-surface-radius, 0) );background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-embedded-surface-bg, transparent) )}.praxis-chart-settings-trigger{position:absolute;top:10px;right:10px;z-index:3
|
|
2579
|
+
`, styles: [":host{display:block;min-height:var(--praxis-chart-runtime-height, 320px);min-width:0;color:var(--md-sys-color-on-surface, #1a1b20)}:host(.praxis-chart-host-fill-container){height:100%;min-height:0}:host-context(.pdx-shell.no-shell) .praxis-chart-shell,:host-context(.pdx-shell.body-fill) .praxis-chart-shell,:host-context(.pdx-shell.expanded) .praxis-chart-shell,:host-context(.pdx-shell.fullscreen) .praxis-chart-shell{height:100%!important}.praxis-chart-shell{position:relative;width:100%;height:100%;min-height:240px;border-radius:var(--praxis-chart-config-surface-radius, var(--praxis-chart-surface-radius, 8px));overflow:hidden;background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-surface-bg, var(--md-sys-color-surface-container-lowest, #fff)) );border-color:var( --praxis-chart-config-surface-border, var( --praxis-chart-surface-border, color-mix(in srgb, var(--md-sys-color-outline, #c5c7ce) 44%, transparent) ) );border-style:solid;border-width:var(--praxis-chart-config-surface-border-width, 1px)}.praxis-chart-shell-fill-container{min-height:0}:host-context(.pdx-shell) .praxis-chart-shell:not(.praxis-chart-shell-contained),.praxis-chart-shell-embedded{border-width:var( --praxis-chart-config-surface-border-width, var(--praxis-chart-embedded-surface-border-width, 0) );border-radius:var( --praxis-chart-config-surface-radius, var(--praxis-chart-embedded-surface-radius, 0) );background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-embedded-surface-bg, transparent) )}.praxis-chart-settings-trigger{position:absolute;top:10px;right:10px;z-index:3;--praxis-icon-button-filled-background: color-mix(in srgb, var(--md-sys-color-surface, #fff) 88%, rgba(18, 99, 180, .12));--praxis-icon-button-filled-foreground: var(--md-sys-color-on-surface-variant, #44474f);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.praxis-chart-host{width:100%;height:100%;min-height:inherit}.praxis-chart-state{height:100%;min-height:min(240px,100%);display:grid;align-content:center;justify-items:center;gap:14px;padding:24px;text-align:center}.praxis-chart-state-copy{display:grid;gap:6px;justify-items:center}.praxis-chart-state-title{font-size:1rem;font-weight:600;color:var(--md-sys-color-on-surface, #1a1b20)}.praxis-chart-state-description{font-size:.925rem;color:var(--md-sys-color-on-surface-variant, #5a5d67);max-width:36rem}.praxis-chart-loading-hero{width:min(100%,320px);display:grid;gap:14px}.praxis-chart-loading-summary{display:flex;gap:8px;justify-content:center}.praxis-chart-loading-chip,.praxis-chart-loading-bar{border-radius:999px;background:linear-gradient(90deg,#1263b414,#1263b438,#1263b414);background-size:200% 100%;animation:praxis-chart-loading-wave 1.2s ease-in-out infinite}.praxis-chart-loading-chip{display:block;width:104px;height:12px}.praxis-chart-loading-chip--short{width:64px}.praxis-chart-loading-plot{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));align-items:end;gap:10px;height:108px;padding:12px 8px 4px;border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container, #eef3f8) 72%,transparent);border:1px solid rgba(18,99,180,.08)}.praxis-chart-loading-bar{display:block;width:100%}.praxis-chart-loading-bar--1{height:32%}.praxis-chart-loading-bar--2{height:68%}.praxis-chart-loading-bar--3{height:48%}.praxis-chart-loading-bar--4{height:78%}.praxis-chart-loading-bar--5{height:56%}@keyframes praxis-chart-loading-wave{0%{background-position:100% 0}to{background-position:-100% 0}}\n"] }]
|
|
2582
2580
|
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], chartDocument: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartDocument", required: false }] }], filterCriteria: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterCriteria", required: false }] }], queryContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryContext", required: false }] }], remoteDataResolver: [{ type: i0.Input, args: [{ isSignal: true, alias: "remoteDataResolver", required: false }] }], enableCustomization: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableCustomization", required: false }] }], availableResources: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableResources", required: false }] }], availableFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableFields", required: false }] }], availableTargets: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableTargets", required: false }] }], pointClick: [{ type: i0.Output, args: ["pointClick"] }], pointAction: [{ type: i0.Output, args: ["pointAction"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], drillDown: [{ type: i0.Output, args: ["drillDown"] }], crossFilter: [{ type: i0.Output, args: ["crossFilter"] }], queryRequest: [{ type: i0.Output, args: ["queryRequest"] }], loadStateChange: [{ type: i0.Output, args: ["loadStateChange"] }], chartDocumentApplied: [{ type: i0.Output, args: ["chartDocumentApplied"] }], chartDocumentSaved: [{ type: i0.Output, args: ["chartDocumentSaved"] }], chartHost: [{ type: i0.ViewChild, args: ['chartHost', { isSignal: true }] }] } });
|
|
2583
2581
|
function mergeRemoteQueryContext(config, queryContext, runtimeFilters) {
|
|
2584
2582
|
const dataSource = config.dataSource;
|
|
@@ -7139,7 +7137,7 @@ class PraxisChartConfigEditor {
|
|
|
7139
7137
|
this.documentChange.emit(structuredClone(this.normalizedDocument()));
|
|
7140
7138
|
}
|
|
7141
7139
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisChartConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7142
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisChartConfigEditor, isStandalone: true, selector: "praxis-chart-config-editor", inputs: { documentInput: { classPropertyName: "documentInput", publicName: "document", isSignal: true, isRequired: false, transformFunction: null }, modeInput: { classPropertyName: "modeInput", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, readonlyInput: { classPropertyName: "readonlyInput", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, availableResourcesInput: { classPropertyName: "availableResourcesInput", publicName: "availableResources", isSignal: true, isRequired: false, transformFunction: null }, availableFieldsInput: { classPropertyName: "availableFieldsInput", publicName: "availableFields", isSignal: true, isRequired: false, transformFunction: null }, availableTargetsInput: { classPropertyName: "availableTargetsInput", publicName: "availableTargets", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply", save: "save", resetChange: "resetChange", documentChange: "documentChange" }, providers: [providePraxisChartsI18n()], ngImport: i0, template: "<div class=\"editor-shell\">\n <div class=\"editor-nav\">\n @for (section of sections; track section.id) {\n <button\n mat-stroked-button\n type=\"button\"\n [attr.data-testid]=\"'chart-editor-section-' + section.id\"\n [class.active]=\"activeSection() === section.id\"\n (click)=\"setSection(section.id)\"\n >\n {{ t(section.labelKey, section.fallback) }}\n </button>\n }\n </div>\n\n <div class=\"editor-layout\">\n <div class=\"editor-form\">\n <mat-card class=\"editor-card\">\n <mat-card-content>\n @switch (activeSection()) {\n @case ('general') {\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.chartId', 'Chart ID') }}</mat-label>\n <input matInput [ngModel]=\"doc().chartId || ''\" (ngModelChange)=\"setChartId($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.kind', 'Kind') }}</mat-label>\n <mat-select [ngModel]=\"doc().kind\" (ngModelChange)=\"setKind($event)\" [disabled]=\"isReadonly()\">\n @for (kind of chartKinds; track kind) {\n <mat-option [value]=\"kind\">\n {{ t('praxis.charts.editor.kind.' + kind, kind) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.title', 'Title') }}</mat-label>\n <input matInput data-testid=\"chart-editor-title-input\" [ngModel]=\"titleValue()\" (ngModelChange)=\"setTitle($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.subtitle', 'Subtitle') }}</mat-label>\n <input matInput [ngModel]=\"subtitleValue()\" (ngModelChange)=\"setSubtitle($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.sizingMode', 'Sizing') }}</mat-label>\n <mat-select [ngModel]=\"sizingModeValue()\" (ngModelChange)=\"setSizingMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of sizingModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.sizing.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n <mat-hint>{{ t('praxis.charts.editor.hint.sizingMode', 'Use fill-container only when the host widget provides a defined body height.') }}</mat-hint>\n </mat-form-field>\n\n @if (sizingModeValue() === 'fixed') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.height', 'Height') }}</mat-label>\n <input matInput [ngModel]=\"heightValue()\" (ngModelChange)=\"setSizingHeight($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.height', 'Numbers are saved as pixels; CSS lengths such as 20rem are also accepted.') }}</mat-hint>\n </mat-form-field>\n }\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.minHeight', 'Minimum height') }}</mat-label>\n <input matInput [ngModel]=\"sizingMinHeightValue()\" (ngModelChange)=\"setSizingMinHeight($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.minHeight', 'Set a readable minimum for compact dashboard widgets.') }}</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.maxHeight', 'Maximum height') }}</mat-label>\n <input matInput [ngModel]=\"sizingMaxHeightValue()\" (ngModelChange)=\"setSizingMaxHeight($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.maxHeight', 'Leave empty unless the chart must stop growing inside a flexible layout.') }}</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.aspectRatio', 'Aspect ratio') }}</mat-label>\n <input matInput [ngModel]=\"sizingAspectRatioValue()\" (ngModelChange)=\"setSizingAspectRatio($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.aspectRatio', 'Optional. Use values such as 1.777 or 16 / 9.') }}</mat-hint>\n </mat-form-field>\n </div>\n }\n\n @case ('data') {\n <div class=\"editor-stack\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.sourceKind', 'Source') }}</mat-label>\n <mat-select data-testid=\"chart-editor-source-kind\" [ngModel]=\"doc().source.kind\" (ngModelChange)=\"setSourceKind($event)\" [disabled]=\"isReadonly()\">\n @for (sourceKind of sourceKinds; track sourceKind) {\n <mat-option [value]=\"sourceKind\">\n {{ t('praxis.charts.editor.sourceKind.' + (sourceKind === 'praxis.stats' ? 'praxisStats' : 'derived'), sourceKind) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (doc().source.kind === 'praxis.stats') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.resource', 'Resource') }}</mat-label>\n <mat-select data-testid=\"chart-editor-resource\" [ngModel]=\"resourceValue()\" (ngModelChange)=\"setResource($event)\" [disabled]=\"isReadonly() || resourceCatalogUnavailable()\">\n @for (resource of resourceOptions(); track resource.id) {\n <mat-option [value]=\"resource.path\">{{ resource.label }}</mat-option>\n }\n </mat-select>\n @if (resourceCatalogUnavailable()) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.resourceMissing', 'Resource catalog is required for governed praxis.stats authoring.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.operation', 'Operation') }}</mat-label>\n <mat-select\n data-testid=\"chart-editor-operation\"\n [ngModel]=\"doc().source.operation || 'group-by'\"\n (ngModelChange)=\"setOperation($event)\"\n [disabled]=\"isReadonly() || operationCatalogUnavailable()\"\n >\n @for (operation of operationOptions(); track operation) {\n <mat-option [value]=\"operation\">\n {{ t('praxis.charts.editor.operation.' + (operation === 'group-by' ? 'groupBy' : operation), operation) }}\n </mat-option>\n }\n </mat-select>\n @if (operationCatalogUnavailable()) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.operationMissing', 'Selected resource does not publish authorable stats operations.') }}</mat-hint>\n }\n </mat-form-field>\n }\n </div>\n\n @if (showTimeseriesControls()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.timeseriesTitle', 'Timeseries options') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.granularity', 'Granularity') }}</mat-label>\n <mat-select [ngModel]=\"granularityValue()\" (ngModelChange)=\"setGranularity($event)\" [disabled]=\"isReadonly()\">\n @for (granularity of timeGranularities; track granularity) {\n <mat-option [value]=\"granularity\">\n {{ t('praxis.charts.editor.granularity.' + granularity, granularity) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-slide-toggle\n [ngModel]=\"fillGapsValue()\"\n (ngModelChange)=\"setFillGaps($event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.fillGaps', 'Fill missing intervals') }}\n </mat-slide-toggle>\n </mat-card-content>\n </mat-card>\n }\n\n @if (showDistributionControls()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.distributionTitle', 'Distribution options') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.distributionMode', 'Distribution mode') }}</mat-label>\n <mat-select data-testid=\"chart-editor-distribution-mode\" [ngModel]=\"distributionModeValue()\" (ngModelChange)=\"setDistributionMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of distributionModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.distributionMode.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (distributionModeValue() === 'histogram') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.bucketSize', 'Bucket size') }}</mat-label>\n <input matInput data-testid=\"chart-editor-bucket-size\" [ngModel]=\"bucketSizeValue()\" (ngModelChange)=\"setBucketSize($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.bucketCount', 'Bucket count') }}</mat-label>\n <input matInput data-testid=\"chart-editor-bucket-count\" [ngModel]=\"bucketCountValue()\" (ngModelChange)=\"setBucketCount($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n }\n </mat-card-content>\n </mat-card>\n }\n\n @if (showComparisonControls()) {\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-comparison-options\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.comparisonTitle', 'Comparison period') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonPeriodField', 'Period field') }}</mat-label>\n @if (fieldOptions('time').length) {\n <mat-select\n [ngModel]=\"comparisonPeriodFieldValue()\"\n (ngModelChange)=\"setComparisonPeriodField($event)\"\n [disabled]=\"isReadonly()\"\n >\n @for (field of fieldOptions('time'); track field.field) {\n <mat-option [value]=\"field.field\">{{ field.label || field.field }}</mat-option>\n }\n </mat-select>\n } @else {\n <mat-select [ngModel]=\"comparisonPeriodFieldValue()\" [disabled]=\"true\"></mat-select>\n <mat-hint>{{ t('praxis.charts.editor.catalog.comparisonPeriodFieldsMissing', 'No governed time-series field is available for comparison periods.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonTimezone', 'Timezone') }}</mat-label>\n <input\n matInput\n [ngModel]=\"comparisonPeriodTimezoneValue()\"\n (ngModelChange)=\"setComparisonPeriodTimezone($event)\"\n [disabled]=\"isReadonly()\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonPreset', 'Period preset') }}</mat-label>\n <mat-select\n [ngModel]=\"comparisonPeriodPresetValue()\"\n (ngModelChange)=\"setComparisonPeriodPreset($event)\"\n [disabled]=\"isReadonly()\"\n >\n @for (preset of comparisonPeriodPresets; track preset) {\n <mat-option [value]=\"preset\">\n {{ t('praxis.charts.editor.comparisonPreset.' + preset, preset) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonMode', 'Comparison mode') }}</mat-label>\n <mat-select\n [ngModel]=\"comparisonPeriodModeValue()\"\n (ngModelChange)=\"setComparisonPeriodMode($event)\"\n [disabled]=\"isReadonly()\"\n >\n @for (mode of comparisonPeriodModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.comparisonMode.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n }\n </div>\n }\n\n @case ('motion') {\n <div class=\"editor-grid\">\n <mat-slide-toggle\n [ngModel]=\"normalizedDocument().motion?.enabled !== false\"\n (ngModelChange)=\"setMotionEnabled($event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.motionEnabled', 'Enable animations') }}\n </mat-slide-toggle>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.motionPreset', 'Motion preset') }}</mat-label>\n <mat-select\n [ngModel]=\"normalizedDocument().motion?.preset || 'standard'\"\n (ngModelChange)=\"setMotionPreset($event)\"\n [disabled]=\"isReadonly() || normalizedDocument().motion?.enabled === false\"\n >\n @for (preset of motionPresets; track preset) {\n <mat-option [value]=\"preset\">\n {{ t('praxis.charts.editor.motionPreset.' + preset, preset) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n\n @case ('appearance') {\n <div class=\"editor-stack\">\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.featuresTitle', 'Display features') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-slide-toggle\n [ngModel]=\"featureEnabled('legend')\"\n (ngModelChange)=\"setFeatureEnabled('legend', $event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.legendEnabled', 'Show legend') }}\n </mat-slide-toggle>\n\n <mat-slide-toggle\n [ngModel]=\"featureEnabled('labels')\"\n (ngModelChange)=\"setFeatureEnabled('labels', $event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.labelsEnabled', 'Show labels') }}\n </mat-slide-toggle>\n\n <mat-slide-toggle\n [ngModel]=\"featureEnabled('tooltip')\"\n (ngModelChange)=\"setFeatureEnabled('tooltip', $event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.tooltipEnabled', 'Show tooltip') }}\n </mat-slide-toggle>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.paletteTitle', 'Palette') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.themeVariant', 'Theme variant') }}</mat-label>\n <mat-select [ngModel]=\"themeVariantValue()\" (ngModelChange)=\"setThemeVariant($event)\" [disabled]=\"isReadonly()\">\n <mat-option [value]=\"''\">\n {{ t('praxis.charts.editor.themeVariant.none', 'No variant') }}\n </mat-option>\n @for (variant of themeVariants; track variant) {\n <mat-option [value]=\"variant\">\n {{ t('praxis.charts.editor.themeVariant.' + variant, variant) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.paletteMode', 'Palette mode') }}</mat-label>\n <mat-select [ngModel]=\"paletteModeValue()\" (ngModelChange)=\"setPaletteMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of paletteModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.paletteMode.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (paletteModeValue() === 'token') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.paletteToken', 'Palette token') }}</mat-label>\n <mat-select [ngModel]=\"paletteTokenValue()\" (ngModelChange)=\"setPaletteToken($event)\" [disabled]=\"isReadonly()\">\n @for (token of paletteTokens; track token) {\n <mat-option [value]=\"token\">\n {{ t('praxis.charts.editor.paletteToken.' + token, token) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.palette', 'Palette colors') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"paletteValue()\"\n (ngModelChange)=\"setPalette($event)\"\n [disabled]=\"isReadonly()\"\n ></textarea>\n </mat-form-field>\n }\n\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.appearance.paletteHint', 'Use a registered token or comma-separated colors to persist theme.palette in the canonical contract.') }}\n </p>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.surfaceTitle', 'Surface') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.surfaceMode', 'Surface mode') }}</mat-label>\n <mat-select [ngModel]=\"surfaceModeValue()\" (ngModelChange)=\"setSurfaceMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of surfaceModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.surface.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.appearance.surfaceHint', 'Use embedded for dashboard widgets and contained only when the chart must own its visual surface.') }}\n </p>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.statesTitle', 'State messages') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n <div class=\"editor-row-card\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.emptyTitle', 'Empty title') }}</mat-label>\n <input matInput [ngModel]=\"stateTitle('empty')\" (ngModelChange)=\"setStateTitle('empty', $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.emptyDescription', 'Empty description') }}</mat-label>\n <textarea matInput rows=\"2\" [ngModel]=\"stateDescription('empty')\" (ngModelChange)=\"setStateDescription('empty', $event)\" [disabled]=\"isReadonly()\"></textarea>\n </mat-form-field>\n </div>\n </div>\n\n <div class=\"editor-row-card\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.loadingTitle', 'Loading title') }}</mat-label>\n <input matInput [ngModel]=\"stateTitle('loading')\" (ngModelChange)=\"setStateTitle('loading', $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.loadingDescription', 'Loading description') }}</mat-label>\n <textarea matInput rows=\"2\" [ngModel]=\"stateDescription('loading')\" (ngModelChange)=\"setStateDescription('loading', $event)\" [disabled]=\"isReadonly()\"></textarea>\n </mat-form-field>\n </div>\n </div>\n\n <div class=\"editor-row-card\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.errorTitle', 'Error title') }}</mat-label>\n <input matInput [ngModel]=\"stateTitle('error')\" (ngModelChange)=\"setStateTitle('error', $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.errorDescription', 'Error description') }}</mat-label>\n <textarea matInput rows=\"2\" [ngModel]=\"stateDescription('error')\" (ngModelChange)=\"setStateDescription('error', $event)\" [disabled]=\"isReadonly()\"></textarea>\n </mat-form-field>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n }\n\n @case ('analytics') {\n <div class=\"editor-stack\">\n @if (showComboPanel()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.comboTitle', 'Combo guidance') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.specialization.comboHint', 'Combo charts require at least two metrics and allow per-metric axis and series kind mapping.') }}\n </p>\n </mat-card-content>\n </mat-card>\n }\n\n @if (showPieDonutPanel()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.pieDonutTitle', 'Composition guidance') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.specialization.pieDonutHint', 'Pie and donut charts keep only the first metric and use the first dimension as the category segment.') }}\n </p>\n </mat-card-content>\n </mat-card>\n }\n\n @if (showScatterPanel()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.scatterTitle', 'Scatter guidance') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.specialization.scatterHint', 'Scatter charts use the first dimension as X and the first metric as Y, so keep both fields mapped.') }}\n </p>\n </mat-card-content>\n </mat-card>\n }\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.analytics.dimensionsTitle', 'Dimensions') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n @for (dimension of dimensions(); track $index) {\n <div class=\"editor-row-card\" [attr.data-testid]=\"'chart-editor-dimension-row-' + $index\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-dimension-field-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.dimension', 'Dimension') }}</mat-label>\n @if (fieldOptions('dimension').length) {\n <mat-select [ngModel]=\"dimension.field || ''\" (ngModelChange)=\"setDimensionField($index, $event)\" [disabled]=\"isReadonly()\">\n @for (field of fieldOptions('dimension'); track field.field) {\n <mat-option [value]=\"field.field\">{{ fieldOptionLabel(field) }}</mat-option>\n }\n </mat-select>\n } @else {\n <mat-select [ngModel]=\"dimension.field || ''\" (ngModelChange)=\"setDimensionField($index, $event)\" [disabled]=\"true\"></mat-select>\n <mat-hint>{{ t('praxis.charts.editor.catalog.dimensionFieldsMissing', 'No governed dimensions are available for the current resource and operation.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-dimension-role-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.dimensionRole', 'Dimension role') }}</mat-label>\n <mat-select [ngModel]=\"dimension.role || 'category'\" (ngModelChange)=\"setDimensionRole($index, $event)\" [disabled]=\"isReadonly()\">\n @for (role of dimensionRoles; track role) {\n <mat-option [value]=\"role\">\n {{ t('praxis.charts.editor.dimensionRole.' + role, role) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"editor-row-actions\">\n <button mat-button type=\"button\" (click)=\"removeDimension($index)\" [disabled]=\"isReadonly() || dimensions().length <= 1\">\n {{ t('praxis.charts.editor.analytics.removeDimension', 'Remove dimension') }}\n </button>\n </div>\n </div>\n }\n\n <div>\n <button mat-stroked-button type=\"button\" data-testid=\"chart-editor-add-dimension\" (click)=\"addDimension()\" [disabled]=\"isReadonly()\">\n {{ t('praxis.charts.editor.analytics.addDimension', 'Add dimension') }}\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.analytics.metricsTitle', 'Metrics') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n @for (metric of metrics(); track $index) {\n <div class=\"editor-row-card\" [attr.data-testid]=\"'chart-editor-metric-row-' + $index\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-field-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metric', 'Metric') }}</mat-label>\n @if (fieldOptions('metric').length) {\n <mat-select [ngModel]=\"metric.field || ''\" (ngModelChange)=\"setMetricField($index, $event)\" [disabled]=\"isReadonly()\">\n @for (field of fieldOptions('metric'); track field.field) {\n <mat-option [value]=\"field.field\">{{ fieldOptionLabel(field) }}</mat-option>\n }\n </mat-select>\n } @else {\n <mat-select [ngModel]=\"metric.field || ''\" (ngModelChange)=\"setMetricField($index, $event)\" [disabled]=\"true\"></mat-select>\n <mat-hint>{{ t('praxis.charts.editor.catalog.metricFieldsMissing', 'No governed metrics are available for the current resource and operation.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-label-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricLabel', 'Metric label') }}</mat-label>\n <input matInput [ngModel]=\"metric.label || ''\" (ngModelChange)=\"setMetricLabel($index, $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-aggregation-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricAggregation', 'Aggregation') }}</mat-label>\n <mat-select [ngModel]=\"metric.aggregation || 'sum'\" (ngModelChange)=\"setMetricAggregation($index, $event)\" [disabled]=\"isReadonly()\">\n @for (aggregation of metricAggregationOptions(metric.field); track aggregation) {\n <mat-option [value]=\"aggregation\">\n {{ t('praxis.charts.editor.metricAggregation.' + aggregation, aggregation) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (showMetricAxisControls()) {\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-axis-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricAxis', 'Axis') }}</mat-label>\n <mat-select [ngModel]=\"metric.axis || 'primary'\" (ngModelChange)=\"setMetricAxis($index, $event)\" [disabled]=\"isReadonly()\">\n @for (axis of metricAxes; track axis) {\n <mat-option [value]=\"axis\">\n {{ t('praxis.charts.editor.metricAxis.' + axis, axis) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (showMetricSeriesKindControls()) {\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-series-kind-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricSeriesKind', 'Series kind') }}</mat-label>\n <mat-select [ngModel]=\"metric.seriesKind || 'bar'\" (ngModelChange)=\"setMetricSeriesKind($index, $event)\" [disabled]=\"isReadonly()\">\n @for (seriesKind of metricSeriesKinds; track seriesKind) {\n <mat-option [value]=\"seriesKind\">\n {{ t('praxis.charts.editor.metricSeriesKind.' + seriesKind, seriesKind) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n\n <div class=\"editor-row-actions\">\n <button mat-button type=\"button\" [attr.data-testid]=\"'chart-editor-remove-metric-' + $index\" (click)=\"removeMetric($index)\" [disabled]=\"isReadonly() || metrics().length <= 1\">\n {{ t('praxis.charts.editor.analytics.removeMetric', 'Remove metric') }}\n </button>\n </div>\n </div>\n }\n\n <div>\n <button mat-stroked-button type=\"button\" data-testid=\"chart-editor-add-metric\" (click)=\"addMetric()\" [disabled]=\"isReadonly()\">\n {{ t('praxis.charts.editor.analytics.addMetric', 'Add metric') }}\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n }\n\n @case ('events') {\n <div class=\"editor-stack\">\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-pointClick\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.pointClickTitle', 'Point click') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-pointClick-action\" [ngModel]=\"eventAction('pointClick')\" (ngModelChange)=\"setEventAction('pointClick', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-pointClick-target\" [ngModel]=\"eventTarget('pointClick')\" (ngModelChange)=\"setEventTarget('pointClick', $event)\" [disabled]=\"isReadonly() || !eventAction('pointClick') || targetCatalogUnavailable(eventAction('pointClick'), 'pointClick')\">\n @for (target of targetOptions(eventAction('pointClick'), 'pointClick'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('pointClick'), 'pointClick')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-pointClick-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('pointClick')\"\n (ngModelChange)=\"setEventMapping('pointClick', $event)\"\n [disabled]=\"isReadonly() || !eventAction('pointClick')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-selectionChange\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.selectionChangeTitle', 'Selection change') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-selectionChange-action\" [ngModel]=\"eventAction('selectionChange')\" (ngModelChange)=\"setEventAction('selectionChange', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-selectionChange-target\" [ngModel]=\"eventTarget('selectionChange')\" (ngModelChange)=\"setEventTarget('selectionChange', $event)\" [disabled]=\"isReadonly() || !eventAction('selectionChange') || targetCatalogUnavailable(eventAction('selectionChange'), 'selectionChange')\">\n @for (target of targetOptions(eventAction('selectionChange'), 'selectionChange'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('selectionChange'), 'selectionChange')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-selectionChange-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('selectionChange')\"\n (ngModelChange)=\"setEventMapping('selectionChange', $event)\"\n [disabled]=\"isReadonly() || !eventAction('selectionChange')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-drillDown\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.drillDownTitle', 'Drill down') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-drillDown-action\" [ngModel]=\"eventAction('drillDown')\" (ngModelChange)=\"setEventAction('drillDown', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-drillDown-target\" [ngModel]=\"eventTarget('drillDown')\" (ngModelChange)=\"setEventTarget('drillDown', $event)\" [disabled]=\"isReadonly() || !eventAction('drillDown') || targetCatalogUnavailable(eventAction('drillDown'), 'drillDown')\">\n @for (target of targetOptions(eventAction('drillDown'), 'drillDown'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('drillDown'), 'drillDown')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-drillDown-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('drillDown')\"\n (ngModelChange)=\"setEventMapping('drillDown', $event)\"\n [disabled]=\"isReadonly() || !eventAction('drillDown')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-crossFilter\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.crossFilterTitle', 'Cross filter') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-crossFilter-action\" [ngModel]=\"eventAction('crossFilter')\" (ngModelChange)=\"setEventAction('crossFilter', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-crossFilter-target\" [ngModel]=\"eventTarget('crossFilter')\" (ngModelChange)=\"setEventTarget('crossFilter', $event)\" [disabled]=\"isReadonly() || !eventAction('crossFilter') || targetCatalogUnavailable(eventAction('crossFilter'), 'crossFilter')\">\n @for (target of targetOptions(eventAction('crossFilter'), 'crossFilter'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('crossFilter'), 'crossFilter')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-crossFilter-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('crossFilter')\"\n (ngModelChange)=\"setEventMapping('crossFilter', $event)\"\n [disabled]=\"isReadonly() || !eventAction('crossFilter')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n </div>\n }\n\n @case ('preview') {\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.preview.caption', 'Local preview derived from the canonical contract without remote calls.') }}\n </p>\n }\n }\n </mat-card-content>\n </mat-card>\n </div>\n\n <div class=\"editor-side\">\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.issues.title', 'Validation issues') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (issues().length) {\n <ul class=\"editor-issues\">\n @for (issue of issues(); track issueTrackBy($index, issue)) {\n <li class=\"editor-issue\">\n <strong>{{ issue.field }}</strong>\n <span>{{ issue.message }}</span>\n </li>\n }\n </ul>\n } @else {\n <div class=\"editor-empty\">\n {{ t('praxis.charts.editor.issues.empty', 'No issues were identified.') }}\n </div>\n }\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.preview.title', 'Chart preview') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (preview(); as chartPreview) {\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.preview.caption', 'Local preview derived from the canonical contract without remote calls.') }}\n </p>\n <praxis-chart [config]=\"chartPreview.config\" [data]=\"chartPreview.data\"></praxis-chart>\n } @else {\n <div class=\"editor-empty\">\n {{ t('praxis.charts.editor.preview.invalid', 'Preview is unavailable while the contract has blocking errors.') }}\n </div>\n }\n </mat-card-content>\n </mat-card>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;min-width:0;container:praxis-chart-editor / inline-size;color:var(--md-sys-color-on-surface, #1a1b20)}.editor-shell{display:grid;gap:18px;min-width:0}.editor-nav{display:flex;gap:8px;flex-wrap:wrap}.editor-nav button.active{background:color-mix(in srgb,var(--md-sys-color-primary, #1263b4) 18%,transparent);color:var(--md-sys-color-primary, #1263b4)}.editor-layout{display:grid;gap:18px;grid-template-columns:minmax(0,1.35fr) minmax(320px,.9fr);align-items:start}.editor-form,.editor-side{display:grid;gap:16px;min-width:0}.editor-card{box-sizing:border-box;width:100%;min-width:0;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 72%,transparent);background:linear-gradient(180deg,#1263b408,#1263b400)}.editor-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-stack{display:grid;gap:14px}.editor-row-card{padding:14px;border-radius:16px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 54%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface, #fff) 92%,rgba(18,99,180,.04))}.editor-row-actions{display:flex;justify-content:flex-end}.editor-field{width:100%}.editor-issues{display:grid;gap:10px;margin:0;padding:0;list-style:none}.editor-issue{padding:12px 14px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-error, #b3261e) 8%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-error, #b3261e) 18%,transparent)}.editor-issue strong{display:block;margin-bottom:4px}.editor-caption{margin:0 0 12px;color:var(--md-sys-color-on-surface-variant, #5a5d67);font-size:.92rem}.editor-empty{padding:18px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-variant, #eceff4) 78%,transparent)}.editor-side praxis-chart{display:block;width:100%;min-width:0;max-width:100%}@container praxis-chart-editor (max-width: 840px){.editor-layout{grid-template-columns:minmax(0,1fr)}}@media(max-width:960px){.editor-layout{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i3$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: PraxisChartComponent, selector: "praxis-chart", inputs: ["config", "data", "chartDocument", "filterCriteria", "queryContext", "remoteDataResolver", "enableCustomization", "availableResources", "availableFields", "availableTargets"], outputs: ["pointClick", "pointAction", "selectionChange", "drillDown", "crossFilter", "queryRequest", "loadStateChange", "chartDocumentApplied", "chartDocumentSaved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7140
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisChartConfigEditor, isStandalone: true, selector: "praxis-chart-config-editor", inputs: { documentInput: { classPropertyName: "documentInput", publicName: "document", isSignal: true, isRequired: false, transformFunction: null }, modeInput: { classPropertyName: "modeInput", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, readonlyInput: { classPropertyName: "readonlyInput", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, availableResourcesInput: { classPropertyName: "availableResourcesInput", publicName: "availableResources", isSignal: true, isRequired: false, transformFunction: null }, availableFieldsInput: { classPropertyName: "availableFieldsInput", publicName: "availableFields", isSignal: true, isRequired: false, transformFunction: null }, availableTargetsInput: { classPropertyName: "availableTargetsInput", publicName: "availableTargets", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply", save: "save", resetChange: "resetChange", documentChange: "documentChange" }, providers: [providePraxisChartsI18n()], ngImport: i0, template: "<div class=\"editor-shell\">\n <div class=\"editor-nav\">\n @for (section of sections; track section.id) {\n <button\n mat-stroked-button\n type=\"button\"\n [attr.data-testid]=\"'chart-editor-section-' + section.id\"\n [class.active]=\"activeSection() === section.id\"\n (click)=\"setSection(section.id)\"\n >\n {{ t(section.labelKey, section.fallback) }}\n </button>\n }\n </div>\n\n <div class=\"editor-layout\">\n <div class=\"editor-form\">\n <mat-card class=\"editor-card\">\n <mat-card-content>\n @switch (activeSection()) {\n @case ('general') {\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.chartId', 'Chart ID') }}</mat-label>\n <input matInput [ngModel]=\"doc().chartId || ''\" (ngModelChange)=\"setChartId($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.kind', 'Kind') }}</mat-label>\n <mat-select [ngModel]=\"doc().kind\" (ngModelChange)=\"setKind($event)\" [disabled]=\"isReadonly()\">\n @for (kind of chartKinds; track kind) {\n <mat-option [value]=\"kind\">\n {{ t('praxis.charts.editor.kind.' + kind, kind) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.title', 'Title') }}</mat-label>\n <input matInput data-testid=\"chart-editor-title-input\" [ngModel]=\"titleValue()\" (ngModelChange)=\"setTitle($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.subtitle', 'Subtitle') }}</mat-label>\n <input matInput [ngModel]=\"subtitleValue()\" (ngModelChange)=\"setSubtitle($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.sizingMode', 'Sizing') }}</mat-label>\n <mat-select [ngModel]=\"sizingModeValue()\" (ngModelChange)=\"setSizingMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of sizingModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.sizing.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n <mat-hint>{{ t('praxis.charts.editor.hint.sizingMode', 'Use fill-container only when the host widget provides a defined body height.') }}</mat-hint>\n </mat-form-field>\n\n @if (sizingModeValue() === 'fixed') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.height', 'Height') }}</mat-label>\n <input matInput [ngModel]=\"heightValue()\" (ngModelChange)=\"setSizingHeight($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.height', 'Numbers are saved as pixels; CSS lengths such as 20rem are also accepted.') }}</mat-hint>\n </mat-form-field>\n }\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.minHeight', 'Minimum height') }}</mat-label>\n <input matInput [ngModel]=\"sizingMinHeightValue()\" (ngModelChange)=\"setSizingMinHeight($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.minHeight', 'Set a readable minimum for compact dashboard widgets.') }}</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.maxHeight', 'Maximum height') }}</mat-label>\n <input matInput [ngModel]=\"sizingMaxHeightValue()\" (ngModelChange)=\"setSizingMaxHeight($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.maxHeight', 'Leave empty unless the chart must stop growing inside a flexible layout.') }}</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.aspectRatio', 'Aspect ratio') }}</mat-label>\n <input matInput [ngModel]=\"sizingAspectRatioValue()\" (ngModelChange)=\"setSizingAspectRatio($event)\" [disabled]=\"isReadonly()\" />\n <mat-hint>{{ t('praxis.charts.editor.hint.aspectRatio', 'Optional. Use values such as 1.777 or 16 / 9.') }}</mat-hint>\n </mat-form-field>\n </div>\n }\n\n @case ('data') {\n <div class=\"editor-stack\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.sourceKind', 'Source') }}</mat-label>\n <mat-select data-testid=\"chart-editor-source-kind\" [ngModel]=\"doc().source.kind\" (ngModelChange)=\"setSourceKind($event)\" [disabled]=\"isReadonly()\">\n @for (sourceKind of sourceKinds; track sourceKind) {\n <mat-option [value]=\"sourceKind\">\n {{ t('praxis.charts.editor.sourceKind.' + (sourceKind === 'praxis.stats' ? 'praxisStats' : 'derived'), sourceKind) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (doc().source.kind === 'praxis.stats') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.resource', 'Resource') }}</mat-label>\n <mat-select data-testid=\"chart-editor-resource\" [ngModel]=\"resourceValue()\" (ngModelChange)=\"setResource($event)\" [disabled]=\"isReadonly() || resourceCatalogUnavailable()\">\n @for (resource of resourceOptions(); track resource.id) {\n <mat-option [value]=\"resource.path\">{{ resource.label }}</mat-option>\n }\n </mat-select>\n @if (resourceCatalogUnavailable()) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.resourceMissing', 'Resource catalog is required for governed praxis.stats authoring.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.operation', 'Operation') }}</mat-label>\n <mat-select\n data-testid=\"chart-editor-operation\"\n [ngModel]=\"doc().source.operation || 'group-by'\"\n (ngModelChange)=\"setOperation($event)\"\n [disabled]=\"isReadonly() || operationCatalogUnavailable()\"\n >\n @for (operation of operationOptions(); track operation) {\n <mat-option [value]=\"operation\">\n {{ t('praxis.charts.editor.operation.' + (operation === 'group-by' ? 'groupBy' : operation), operation) }}\n </mat-option>\n }\n </mat-select>\n @if (operationCatalogUnavailable()) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.operationMissing', 'Selected resource does not publish authorable stats operations.') }}</mat-hint>\n }\n </mat-form-field>\n }\n </div>\n\n @if (showTimeseriesControls()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.timeseriesTitle', 'Timeseries options') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.granularity', 'Granularity') }}</mat-label>\n <mat-select [ngModel]=\"granularityValue()\" (ngModelChange)=\"setGranularity($event)\" [disabled]=\"isReadonly()\">\n @for (granularity of timeGranularities; track granularity) {\n <mat-option [value]=\"granularity\">\n {{ t('praxis.charts.editor.granularity.' + granularity, granularity) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-slide-toggle\n [ngModel]=\"fillGapsValue()\"\n (ngModelChange)=\"setFillGaps($event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.fillGaps', 'Fill missing intervals') }}\n </mat-slide-toggle>\n </mat-card-content>\n </mat-card>\n }\n\n @if (showDistributionControls()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.distributionTitle', 'Distribution options') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.distributionMode', 'Distribution mode') }}</mat-label>\n <mat-select data-testid=\"chart-editor-distribution-mode\" [ngModel]=\"distributionModeValue()\" (ngModelChange)=\"setDistributionMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of distributionModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.distributionMode.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (distributionModeValue() === 'histogram') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.bucketSize', 'Bucket size') }}</mat-label>\n <input matInput data-testid=\"chart-editor-bucket-size\" [ngModel]=\"bucketSizeValue()\" (ngModelChange)=\"setBucketSize($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.bucketCount', 'Bucket count') }}</mat-label>\n <input matInput data-testid=\"chart-editor-bucket-count\" [ngModel]=\"bucketCountValue()\" (ngModelChange)=\"setBucketCount($event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n }\n </mat-card-content>\n </mat-card>\n }\n\n @if (showComparisonControls()) {\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-comparison-options\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.comparisonTitle', 'Comparison period') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonPeriodField', 'Period field') }}</mat-label>\n @if (fieldOptions('time').length) {\n <mat-select\n [ngModel]=\"comparisonPeriodFieldValue()\"\n (ngModelChange)=\"setComparisonPeriodField($event)\"\n [disabled]=\"isReadonly()\"\n >\n @for (field of fieldOptions('time'); track field.field) {\n <mat-option [value]=\"field.field\">{{ field.label || field.field }}</mat-option>\n }\n </mat-select>\n } @else {\n <mat-select [ngModel]=\"comparisonPeriodFieldValue()\" [disabled]=\"true\"></mat-select>\n <mat-hint>{{ t('praxis.charts.editor.catalog.comparisonPeriodFieldsMissing', 'No governed time-series field is available for comparison periods.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonTimezone', 'Timezone') }}</mat-label>\n <input\n matInput\n [ngModel]=\"comparisonPeriodTimezoneValue()\"\n (ngModelChange)=\"setComparisonPeriodTimezone($event)\"\n [disabled]=\"isReadonly()\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonPreset', 'Period preset') }}</mat-label>\n <mat-select\n [ngModel]=\"comparisonPeriodPresetValue()\"\n (ngModelChange)=\"setComparisonPeriodPreset($event)\"\n [disabled]=\"isReadonly()\"\n >\n @for (preset of comparisonPeriodPresets; track preset) {\n <mat-option [value]=\"preset\">\n {{ t('praxis.charts.editor.comparisonPreset.' + preset, preset) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.comparisonMode', 'Comparison mode') }}</mat-label>\n <mat-select\n [ngModel]=\"comparisonPeriodModeValue()\"\n (ngModelChange)=\"setComparisonPeriodMode($event)\"\n [disabled]=\"isReadonly()\"\n >\n @for (mode of comparisonPeriodModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.comparisonMode.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n }\n </div>\n }\n\n @case ('motion') {\n <div class=\"editor-grid\">\n <mat-slide-toggle\n [ngModel]=\"normalizedDocument().motion?.enabled !== false\"\n (ngModelChange)=\"setMotionEnabled($event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.motionEnabled', 'Enable animations') }}\n </mat-slide-toggle>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.motionPreset', 'Motion preset') }}</mat-label>\n <mat-select\n [ngModel]=\"normalizedDocument().motion?.preset || 'standard'\"\n (ngModelChange)=\"setMotionPreset($event)\"\n [disabled]=\"isReadonly() || normalizedDocument().motion?.enabled === false\"\n >\n @for (preset of motionPresets; track preset) {\n <mat-option [value]=\"preset\">\n {{ t('praxis.charts.editor.motionPreset.' + preset, preset) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n\n @case ('appearance') {\n <div class=\"editor-stack\">\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.featuresTitle', 'Display features') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-slide-toggle\n [ngModel]=\"featureEnabled('legend')\"\n (ngModelChange)=\"setFeatureEnabled('legend', $event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.legendEnabled', 'Show legend') }}\n </mat-slide-toggle>\n\n <mat-slide-toggle\n [ngModel]=\"featureEnabled('labels')\"\n (ngModelChange)=\"setFeatureEnabled('labels', $event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.labelsEnabled', 'Show labels') }}\n </mat-slide-toggle>\n\n <mat-slide-toggle\n [ngModel]=\"featureEnabled('tooltip')\"\n (ngModelChange)=\"setFeatureEnabled('tooltip', $event)\"\n [disabled]=\"isReadonly()\"\n >\n {{ t('praxis.charts.editor.field.tooltipEnabled', 'Show tooltip') }}\n </mat-slide-toggle>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.paletteTitle', 'Palette') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.themeVariant', 'Theme variant') }}</mat-label>\n <mat-select [ngModel]=\"themeVariantValue()\" (ngModelChange)=\"setThemeVariant($event)\" [disabled]=\"isReadonly()\">\n <mat-option [value]=\"''\">\n {{ t('praxis.charts.editor.themeVariant.none', 'No variant') }}\n </mat-option>\n @for (variant of themeVariants; track variant) {\n <mat-option [value]=\"variant\">\n {{ t('praxis.charts.editor.themeVariant.' + variant, variant) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.paletteMode', 'Palette mode') }}</mat-label>\n <mat-select [ngModel]=\"paletteModeValue()\" (ngModelChange)=\"setPaletteMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of paletteModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.paletteMode.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (paletteModeValue() === 'token') {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.paletteToken', 'Palette token') }}</mat-label>\n <mat-select [ngModel]=\"paletteTokenValue()\" (ngModelChange)=\"setPaletteToken($event)\" [disabled]=\"isReadonly()\">\n @for (token of paletteTokens; track token) {\n <mat-option [value]=\"token\">\n {{ t('praxis.charts.editor.paletteToken.' + token, token) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.palette', 'Palette colors') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"paletteValue()\"\n (ngModelChange)=\"setPalette($event)\"\n [disabled]=\"isReadonly()\"\n ></textarea>\n </mat-form-field>\n }\n\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.appearance.paletteHint', 'Use a registered token or comma-separated colors to persist theme.palette in the canonical contract.') }}\n </p>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.surfaceTitle', 'Surface') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.surfaceMode', 'Surface mode') }}</mat-label>\n <mat-select [ngModel]=\"surfaceModeValue()\" (ngModelChange)=\"setSurfaceMode($event)\" [disabled]=\"isReadonly()\">\n @for (mode of surfaceModes; track mode) {\n <mat-option [value]=\"mode\">\n {{ t('praxis.charts.editor.surface.' + mode, mode) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.appearance.surfaceHint', 'Use embedded for dashboard widgets and contained only when the chart must own its visual surface.') }}\n </p>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.appearance.statesTitle', 'State messages') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n <div class=\"editor-row-card\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.emptyTitle', 'Empty title') }}</mat-label>\n <input matInput [ngModel]=\"stateTitle('empty')\" (ngModelChange)=\"setStateTitle('empty', $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.emptyDescription', 'Empty description') }}</mat-label>\n <textarea matInput rows=\"2\" [ngModel]=\"stateDescription('empty')\" (ngModelChange)=\"setStateDescription('empty', $event)\" [disabled]=\"isReadonly()\"></textarea>\n </mat-form-field>\n </div>\n </div>\n\n <div class=\"editor-row-card\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.loadingTitle', 'Loading title') }}</mat-label>\n <input matInput [ngModel]=\"stateTitle('loading')\" (ngModelChange)=\"setStateTitle('loading', $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.loadingDescription', 'Loading description') }}</mat-label>\n <textarea matInput rows=\"2\" [ngModel]=\"stateDescription('loading')\" (ngModelChange)=\"setStateDescription('loading', $event)\" [disabled]=\"isReadonly()\"></textarea>\n </mat-form-field>\n </div>\n </div>\n\n <div class=\"editor-row-card\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.errorTitle', 'Error title') }}</mat-label>\n <input matInput [ngModel]=\"stateTitle('error')\" (ngModelChange)=\"setStateTitle('error', $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.errorDescription', 'Error description') }}</mat-label>\n <textarea matInput rows=\"2\" [ngModel]=\"stateDescription('error')\" (ngModelChange)=\"setStateDescription('error', $event)\" [disabled]=\"isReadonly()\"></textarea>\n </mat-form-field>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n }\n\n @case ('analytics') {\n <div class=\"editor-stack\">\n @if (showComboPanel()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.comboTitle', 'Combo guidance') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.specialization.comboHint', 'Combo charts require at least two metrics and allow per-metric axis and series kind mapping.') }}\n </p>\n </mat-card-content>\n </mat-card>\n }\n\n @if (showPieDonutPanel()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.pieDonutTitle', 'Composition guidance') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.specialization.pieDonutHint', 'Pie and donut charts keep only the first metric and use the first dimension as the category segment.') }}\n </p>\n </mat-card-content>\n </mat-card>\n }\n\n @if (showScatterPanel()) {\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.specialization.scatterTitle', 'Scatter guidance') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.specialization.scatterHint', 'Scatter charts use the first dimension as X and the first metric as Y, so keep both fields mapped.') }}\n </p>\n </mat-card-content>\n </mat-card>\n }\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.analytics.dimensionsTitle', 'Dimensions') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n @for (dimension of dimensions(); track $index) {\n <div class=\"editor-row-card\" [attr.data-testid]=\"'chart-editor-dimension-row-' + $index\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-dimension-field-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.dimension', 'Dimension') }}</mat-label>\n @if (fieldOptions('dimension').length) {\n <mat-select [ngModel]=\"dimension.field || ''\" (ngModelChange)=\"setDimensionField($index, $event)\" [disabled]=\"isReadonly()\">\n @for (field of fieldOptions('dimension'); track field.field) {\n <mat-option [value]=\"field.field\">{{ fieldOptionLabel(field) }}</mat-option>\n }\n </mat-select>\n } @else {\n <mat-select [ngModel]=\"dimension.field || ''\" (ngModelChange)=\"setDimensionField($index, $event)\" [disabled]=\"true\"></mat-select>\n <mat-hint>{{ t('praxis.charts.editor.catalog.dimensionFieldsMissing', 'No governed dimensions are available for the current resource and operation.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-dimension-role-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.dimensionRole', 'Dimension role') }}</mat-label>\n <mat-select [ngModel]=\"dimension.role || 'category'\" (ngModelChange)=\"setDimensionRole($index, $event)\" [disabled]=\"isReadonly()\">\n @for (role of dimensionRoles; track role) {\n <mat-option [value]=\"role\">\n {{ t('praxis.charts.editor.dimensionRole.' + role, role) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"editor-row-actions\">\n <button mat-button type=\"button\" (click)=\"removeDimension($index)\" [disabled]=\"isReadonly() || dimensions().length <= 1\">\n {{ t('praxis.charts.editor.analytics.removeDimension', 'Remove dimension') }}\n </button>\n </div>\n </div>\n }\n\n <div>\n <button mat-stroked-button type=\"button\" data-testid=\"chart-editor-add-dimension\" (click)=\"addDimension()\" [disabled]=\"isReadonly()\">\n {{ t('praxis.charts.editor.analytics.addDimension', 'Add dimension') }}\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.analytics.metricsTitle', 'Metrics') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-stack\">\n @for (metric of metrics(); track $index) {\n <div class=\"editor-row-card\" [attr.data-testid]=\"'chart-editor-metric-row-' + $index\">\n <div class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-field-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metric', 'Metric') }}</mat-label>\n @if (fieldOptions('metric').length) {\n <mat-select [ngModel]=\"metric.field || ''\" (ngModelChange)=\"setMetricField($index, $event)\" [disabled]=\"isReadonly()\">\n @for (field of fieldOptions('metric'); track field.field) {\n <mat-option [value]=\"field.field\">{{ fieldOptionLabel(field) }}</mat-option>\n }\n </mat-select>\n } @else {\n <mat-select [ngModel]=\"metric.field || ''\" (ngModelChange)=\"setMetricField($index, $event)\" [disabled]=\"true\"></mat-select>\n <mat-hint>{{ t('praxis.charts.editor.catalog.metricFieldsMissing', 'No governed metrics are available for the current resource and operation.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-label-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricLabel', 'Metric label') }}</mat-label>\n <input matInput [ngModel]=\"metric.label || ''\" (ngModelChange)=\"setMetricLabel($index, $event)\" [disabled]=\"isReadonly()\" />\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-aggregation-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricAggregation', 'Aggregation') }}</mat-label>\n <mat-select [ngModel]=\"metric.aggregation || 'sum'\" (ngModelChange)=\"setMetricAggregation($index, $event)\" [disabled]=\"isReadonly()\">\n @for (aggregation of metricAggregationOptions(metric.field); track aggregation) {\n <mat-option [value]=\"aggregation\">\n {{ t('praxis.charts.editor.metricAggregation.' + aggregation, aggregation) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n @if (showMetricAxisControls()) {\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-axis-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricAxis', 'Axis') }}</mat-label>\n <mat-select [ngModel]=\"metric.axis || 'primary'\" (ngModelChange)=\"setMetricAxis($index, $event)\" [disabled]=\"isReadonly()\">\n @for (axis of metricAxes; track axis) {\n <mat-option [value]=\"axis\">\n {{ t('praxis.charts.editor.metricAxis.' + axis, axis) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (showMetricSeriesKindControls()) {\n <mat-form-field class=\"editor-field\" appearance=\"outline\" [attr.data-testid]=\"'chart-editor-metric-series-kind-' + $index\">\n <mat-label>{{ t('praxis.charts.editor.field.metricSeriesKind', 'Series kind') }}</mat-label>\n <mat-select [ngModel]=\"metric.seriesKind || 'bar'\" (ngModelChange)=\"setMetricSeriesKind($index, $event)\" [disabled]=\"isReadonly()\">\n @for (seriesKind of metricSeriesKinds; track seriesKind) {\n <mat-option [value]=\"seriesKind\">\n {{ t('praxis.charts.editor.metricSeriesKind.' + seriesKind, seriesKind) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n\n <div class=\"editor-row-actions\">\n <button mat-button type=\"button\" [attr.data-testid]=\"'chart-editor-remove-metric-' + $index\" (click)=\"removeMetric($index)\" [disabled]=\"isReadonly() || metrics().length <= 1\">\n {{ t('praxis.charts.editor.analytics.removeMetric', 'Remove metric') }}\n </button>\n </div>\n </div>\n }\n\n <div>\n <button mat-stroked-button type=\"button\" data-testid=\"chart-editor-add-metric\" (click)=\"addMetric()\" [disabled]=\"isReadonly()\">\n {{ t('praxis.charts.editor.analytics.addMetric', 'Add metric') }}\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n }\n\n @case ('events') {\n <div class=\"editor-stack\">\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-pointClick\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.pointClickTitle', 'Point click') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-pointClick-action\" [ngModel]=\"eventAction('pointClick')\" (ngModelChange)=\"setEventAction('pointClick', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-pointClick-target\" [ngModel]=\"eventTarget('pointClick')\" (ngModelChange)=\"setEventTarget('pointClick', $event)\" [disabled]=\"isReadonly() || !eventAction('pointClick') || targetCatalogUnavailable(eventAction('pointClick'), 'pointClick')\">\n @for (target of targetOptions(eventAction('pointClick'), 'pointClick'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('pointClick'), 'pointClick')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-pointClick-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('pointClick')\"\n (ngModelChange)=\"setEventMapping('pointClick', $event)\"\n [disabled]=\"isReadonly() || !eventAction('pointClick')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-selectionChange\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.selectionChangeTitle', 'Selection change') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-selectionChange-action\" [ngModel]=\"eventAction('selectionChange')\" (ngModelChange)=\"setEventAction('selectionChange', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-selectionChange-target\" [ngModel]=\"eventTarget('selectionChange')\" (ngModelChange)=\"setEventTarget('selectionChange', $event)\" [disabled]=\"isReadonly() || !eventAction('selectionChange') || targetCatalogUnavailable(eventAction('selectionChange'), 'selectionChange')\">\n @for (target of targetOptions(eventAction('selectionChange'), 'selectionChange'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('selectionChange'), 'selectionChange')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-selectionChange-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('selectionChange')\"\n (ngModelChange)=\"setEventMapping('selectionChange', $event)\"\n [disabled]=\"isReadonly() || !eventAction('selectionChange')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-drillDown\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.drillDownTitle', 'Drill down') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-drillDown-action\" [ngModel]=\"eventAction('drillDown')\" (ngModelChange)=\"setEventAction('drillDown', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-drillDown-target\" [ngModel]=\"eventTarget('drillDown')\" (ngModelChange)=\"setEventTarget('drillDown', $event)\" [disabled]=\"isReadonly() || !eventAction('drillDown') || targetCatalogUnavailable(eventAction('drillDown'), 'drillDown')\">\n @for (target of targetOptions(eventAction('drillDown'), 'drillDown'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('drillDown'), 'drillDown')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-drillDown-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('drillDown')\"\n (ngModelChange)=\"setEventMapping('drillDown', $event)\"\n [disabled]=\"isReadonly() || !eventAction('drillDown')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\" data-testid=\"chart-editor-event-crossFilter\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.events.crossFilterTitle', 'Cross filter') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content class=\"editor-grid\">\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventAction', 'Action') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-crossFilter-action\" [ngModel]=\"eventAction('crossFilter')\" (ngModelChange)=\"setEventAction('crossFilter', $event)\" [disabled]=\"isReadonly()\">\n <mat-option value=\"\">{{ t('praxis.charts.editor.events.none', 'None') }}</mat-option>\n @for (action of eventActionOptions; track action) {\n <mat-option [value]=\"action\">\n {{ t('praxis.charts.editor.eventAction.' + action, action) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventTarget', 'Target') }}</mat-label>\n <mat-select data-testid=\"chart-editor-event-crossFilter-target\" [ngModel]=\"eventTarget('crossFilter')\" (ngModelChange)=\"setEventTarget('crossFilter', $event)\" [disabled]=\"isReadonly() || !eventAction('crossFilter') || targetCatalogUnavailable(eventAction('crossFilter'), 'crossFilter')\">\n @for (target of targetOptions(eventAction('crossFilter'), 'crossFilter'); track target.id) {\n <mat-option [value]=\"target.id\">{{ target.label }}</mat-option>\n }\n </mat-select>\n @if (targetCatalogUnavailable(eventAction('crossFilter'), 'crossFilter')) {\n <mat-hint>{{ t('praxis.charts.editor.catalog.targetMissing', 'Target catalog is required for this governed event action.') }}</mat-hint>\n }\n </mat-form-field>\n\n <mat-form-field class=\"editor-field\" appearance=\"outline\">\n <mat-label>{{ t('praxis.charts.editor.field.eventMapping', 'Mapping') }}</mat-label>\n <textarea\n matInput\n data-testid=\"chart-editor-event-crossFilter-mapping\"\n rows=\"4\"\n [ngModel]=\"eventMappingText('crossFilter')\"\n (ngModelChange)=\"setEventMapping('crossFilter', $event)\"\n [disabled]=\"isReadonly() || !eventAction('crossFilter')\"\n ></textarea>\n </mat-form-field>\n </mat-card-content>\n </mat-card>\n </div>\n }\n\n @case ('preview') {\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.preview.caption', 'Local preview derived from the canonical contract without remote calls.') }}\n </p>\n }\n }\n </mat-card-content>\n </mat-card>\n </div>\n\n <div class=\"editor-side\">\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.issues.title', 'Validation issues') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (issues().length) {\n <ul class=\"editor-issues\">\n @for (issue of issues(); track issueTrackBy($index, issue)) {\n <li class=\"editor-issue\">\n <strong>{{ issue.field }}</strong>\n <span>{{ issue.message }}</span>\n </li>\n }\n </ul>\n } @else {\n <div class=\"editor-empty\">\n {{ t('praxis.charts.editor.issues.empty', 'No issues were identified.') }}\n </div>\n }\n </mat-card-content>\n </mat-card>\n\n <mat-card class=\"editor-card\">\n <mat-card-header>\n <mat-card-title>{{ t('praxis.charts.editor.preview.title', 'Chart preview') }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (preview(); as chartPreview) {\n <p class=\"editor-caption\">\n {{ t('praxis.charts.editor.preview.caption', 'Local preview derived from the canonical contract without remote calls.') }}\n </p>\n <praxis-chart [config]=\"chartPreview.config\" [data]=\"chartPreview.data\"></praxis-chart>\n } @else {\n <div class=\"editor-empty\">\n {{ t('praxis.charts.editor.preview.invalid', 'Preview is unavailable while the contract has blocking errors.') }}\n </div>\n }\n </mat-card-content>\n </mat-card>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;min-width:0;container:praxis-chart-editor / inline-size;color:var(--md-sys-color-on-surface, #1a1b20)}.editor-shell{display:grid;gap:18px;min-width:0}.editor-nav{display:flex;gap:8px;flex-wrap:wrap}.editor-nav button.active{background:color-mix(in srgb,var(--md-sys-color-primary, #1263b4) 18%,transparent);color:var(--md-sys-color-primary, #1263b4)}.editor-layout{display:grid;gap:18px;grid-template-columns:minmax(0,1.35fr) minmax(320px,.9fr);align-items:start}.editor-form,.editor-side{display:grid;gap:16px;min-width:0}.editor-card{box-sizing:border-box;width:100%;min-width:0;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 72%,transparent);background:linear-gradient(180deg,#1263b408,#1263b400)}.editor-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-stack{display:grid;gap:14px}.editor-row-card{padding:14px;border-radius:16px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 54%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface, #fff) 92%,rgba(18,99,180,.04))}.editor-row-actions{display:flex;justify-content:flex-end}.editor-field{width:100%}.editor-issues{display:grid;gap:10px;margin:0;padding:0;list-style:none}.editor-issue{padding:12px 14px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-error, #b3261e) 8%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-error, #b3261e) 18%,transparent)}.editor-issue strong{display:block;margin-bottom:4px}.editor-caption{margin:0 0 12px;color:var(--md-sys-color-on-surface-variant, #5a5d67);font-size:.92rem}.editor-empty{padding:18px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-variant, #eceff4) 78%,transparent)}.editor-side praxis-chart{display:block;width:100%;min-width:0;max-width:100%}@container praxis-chart-editor (max-width: 840px){.editor-layout{grid-template-columns:minmax(0,1fr)}}@media(max-width:960px){.editor-layout{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i3.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: PraxisChartComponent, selector: "praxis-chart", inputs: ["config", "data", "chartDocument", "filterCriteria", "queryContext", "remoteDataResolver", "enableCustomization", "availableResources", "availableFields", "availableTargets"], outputs: ["pointClick", "pointAction", "selectionChange", "drillDown", "crossFilter", "queryRequest", "loadStateChange", "chartDocumentApplied", "chartDocumentSaved"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7143
7141
|
}
|
|
7144
7142
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisChartConfigEditor, decorators: [{
|
|
7145
7143
|
type: Component,
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/charts",
|
|
3
|
-
"version": "9.0.0-beta.
|
|
3
|
+
"version": "9.0.0-beta.88",
|
|
4
4
|
"description": "Metadata-driven charts library for Praxis UI Angular with engine adapters and Apache ECharts as the initial renderer.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^21.0.0",
|
|
7
7
|
"@angular/core": "^21.0.0",
|
|
8
|
-
"@praxisui/core": "^9.0.0-beta.
|
|
8
|
+
"@praxisui/core": "^9.0.0-beta.88",
|
|
9
9
|
"@angular/forms": "^21.0.0",
|
|
10
10
|
"@angular/material": "^21.0.0",
|
|
11
|
-
"@praxisui/table": "^9.0.0-beta.
|
|
11
|
+
"@praxisui/table": "^9.0.0-beta.88",
|
|
12
12
|
"rxjs": "~7.8.0"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|