@praxisui/charts 9.0.38 → 9.0.39

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/README.md CHANGED
@@ -142,6 +142,18 @@ Use the component with either local data or governed remote execution:
142
142
 
143
143
  The public contract is `PraxisChartConfig` or the canonical `PraxisXUiChartContract`; raw ECharts options are adapter detail, not the host-facing model.
144
144
 
145
+ ## Accessibility
146
+
147
+ `PraxisChartComponent` materializes accessibility from the same canonical config and transformed rows used by the visual renderer:
148
+
149
+ - the default ECharts adapter enables its engine-owned ARIA description without exposing ECharts options through the public contract;
150
+ - every ready chart offers a localized `Show chart data` / `Exibir dados do gráfico` control with a structured HTML table;
151
+ - each table point is a native button, so Enter and Space follow browser keyboard conventions and activate the same renderer-neutral `pointClick` path as the visual point;
152
+ - when single selection is configured, the point button publishes `aria-pressed` and preserves the existing select/deselect and cross-filter payloads;
153
+ - loading, empty and error states remove the data controls, preventing stale chart values from remaining available to assistive technology.
154
+
155
+ The structured alternative is automatic. It does not add an accessibility field to `PraxisChartConfig` or `x-ui.chart`, and custom engines remain responsible for an accessible visual rendering while the component-level data alternative remains renderer-neutral.
156
+
145
157
  Supported canonical kinds are `bar`, `horizontal-bar`, `line`, `area`, `stacked-bar`,
146
158
  `stacked-area`, `combo`, `pie`, `donut`, `scatter`, `funnel`, `pyramid`, `treemap`, and `gauge`. Funnel,
147
159
  pyramid, and treemap require one categorical dimension and exactly one non-negative metric. Funnel
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-08-30T02:26:55.462Z",
3
+ "generatedAt": "2026-08-30T06:49:42.426Z",
4
4
  "packageName": "@praxisui/charts",
5
- "packageVersion": "9.0.38",
5
+ "packageVersion": "9.0.39",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 3,
@@ -1834,6 +1834,7 @@ const DEFAULT_CHART_CONFIG = {
1834
1834
  },
1835
1835
  series: [],
1836
1836
  };
1837
+ let chartAccessibilityPanelSequence = 0;
1837
1838
  class PraxisChartComponent {
1838
1839
  config = input(DEFAULT_CHART_CONFIG, ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
1839
1840
  data = input(null, ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
@@ -1879,6 +1880,9 @@ class PraxisChartComponent {
1879
1880
  mappedRuntimeConfig = signal(null, ...(ngDevMode ? [{ debugName: "mappedRuntimeConfig" }] : /* istanbul ignore next */ []));
1880
1881
  chartDocumentMappingError = signal(null, ...(ngDevMode ? [{ debugName: "chartDocumentMappingError" }] : /* istanbul ignore next */ []));
1881
1882
  activeSelectionSignature = signal(null, ...(ngDevMode ? [{ debugName: "activeSelectionSignature" }] : /* istanbul ignore next */ []));
1883
+ showAccessibleData = signal(false, ...(ngDevMode ? [{ debugName: "showAccessibleData" }] : /* istanbul ignore next */ []));
1884
+ accessibilityPanelId = `praxis-chart-data-${++chartAccessibilityPanelSequence}`;
1885
+ accessibilityPanelTitleId = `${this.accessibilityPanelId}-title`;
1882
1886
  fillContainerHeight = signal(false, ...(ngDevMode ? [{ debugName: "fillContainerHeight" }] : /* istanbul ignore next */ []));
1883
1887
  renderAttempt = signal(0, ...(ngDevMode ? [{ debugName: "renderAttempt" }] : /* istanbul ignore next */ []));
1884
1888
  resizeFrameId = null;
@@ -2002,6 +2006,7 @@ class PraxisChartComponent {
2002
2006
  }, ...(ngDevMode ? [{ debugName: "renderConfig" }] : /* istanbul ignore next */ []));
2003
2007
  loadingLabel = computed(() => this.i18n.resolve(this.renderConfig().state?.loadingLabel), ...(ngDevMode ? [{ debugName: "loadingLabel" }] : /* istanbul ignore next */ []));
2004
2008
  transformedData = computed(() => this.transformer.transform(this.renderConfig(), this.resolvedData()), ...(ngDevMode ? [{ debugName: "transformedData" }] : /* istanbul ignore next */ []));
2009
+ accessiblePoints = computed(() => buildAccessibleChartPoints(this.renderConfig(), this.transformedData(), (value) => this.formatAccessibleValue(value), (index) => this.t('praxis.charts.runtime.accessibility.pointFallback', `Point ${index + 1}`, { index: index + 1 }), () => this.t('praxis.charts.runtime.accessibility.seriesFallback', 'Series')), ...(ngDevMode ? [{ debugName: "accessiblePoints" }] : /* istanbul ignore next */ []));
2005
2010
  dataDiagnostic = computed(() => {
2006
2011
  const config = this.renderConfig();
2007
2012
  const explicitData = this.data();
@@ -2027,6 +2032,16 @@ class PraxisChartComponent {
2027
2032
  : this.i18n.resolve(this.renderConfig().state?.error?.description)
2028
2033
  || this.t('praxis.charts.runtime.remoteErrorDescription', 'Revise a fonte de dados e os filtros antes de continuar.'), ...(ngDevMode ? [{ debugName: "errorDescription" }] : /* istanbul ignore next */ []));
2029
2034
  editChartLabel = computed(() => this.t('praxis.charts.runtime.editChart', 'Edit chart settings'), ...(ngDevMode ? [{ debugName: "editChartLabel" }] : /* istanbul ignore next */ []));
2035
+ accessibleDataToggleLabel = computed(() => this.showAccessibleData()
2036
+ ? this.t('praxis.charts.runtime.accessibility.hideData', 'Hide chart data')
2037
+ : this.t('praxis.charts.runtime.accessibility.showData', 'Show chart data'), ...(ngDevMode ? [{ debugName: "accessibleDataToggleLabel" }] : /* istanbul ignore next */ []));
2038
+ accessibleDataTitle = computed(() => this.i18n.resolve(this.renderConfig().title)
2039
+ || this.t('praxis.charts.runtime.accessibility.dataTitle', 'Chart data'), ...(ngDevMode ? [{ debugName: "accessibleDataTitle" }] : /* istanbul ignore next */ []));
2040
+ accessibleDataDescription = computed(() => this.i18n.resolve(this.renderConfig().subtitle)
2041
+ || this.t('praxis.charts.runtime.accessibility.dataDescription', 'Structured alternative for the values represented in the chart.'), ...(ngDevMode ? [{ debugName: "accessibleDataDescription" }] : /* istanbul ignore next */ []));
2042
+ accessibleCategoryHeading = computed(() => this.t('praxis.charts.runtime.accessibility.category', 'Category'), ...(ngDevMode ? [{ debugName: "accessibleCategoryHeading" }] : /* istanbul ignore next */ []));
2043
+ accessibleSeriesHeading = computed(() => this.t('praxis.charts.runtime.accessibility.series', 'Series'), ...(ngDevMode ? [{ debugName: "accessibleSeriesHeading" }] : /* istanbul ignore next */ []));
2044
+ accessibleValueHeading = computed(() => this.t('praxis.charts.runtime.accessibility.value', 'Value'), ...(ngDevMode ? [{ debugName: "accessibleValueHeading" }] : /* istanbul ignore next */ []));
2030
2045
  loadState = computed(() => {
2031
2046
  if (this.chartDocumentMappingError()) {
2032
2047
  return 'error';
@@ -2114,6 +2129,9 @@ class PraxisChartComponent {
2114
2129
  });
2115
2130
  effect(() => {
2116
2131
  const nextState = this.loadState();
2132
+ if (nextState !== 'ready' || !this.accessiblePoints().length) {
2133
+ this.showAccessibleData.set(false);
2134
+ }
2117
2135
  if (this.currentLoadState() !== nextState) {
2118
2136
  this.currentLoadState.set(nextState);
2119
2137
  this.loadStateChange.emit(nextState);
@@ -2233,8 +2251,23 @@ class PraxisChartComponent {
2233
2251
  const description = descriptions[diagnostic.code];
2234
2252
  return this.t(description.key, description.text);
2235
2253
  }
2236
- t(key, fallback) {
2237
- return this.i18n.t(key, undefined, fallback, PRAXIS_CHARTS_I18N_NAMESPACE);
2254
+ formatAccessibleValue(value) {
2255
+ if (Array.isArray(value)) {
2256
+ return value.map((item) => this.formatAccessibleValue(item)).join(', ');
2257
+ }
2258
+ if (value instanceof Date) {
2259
+ return this.i18n.formatDate(value);
2260
+ }
2261
+ if (typeof value === 'number') {
2262
+ return this.i18n.formatNumber(value);
2263
+ }
2264
+ if (value === null || value === undefined || value === '') {
2265
+ return this.t('praxis.charts.runtime.accessibility.notAvailable', 'Not available');
2266
+ }
2267
+ return String(value);
2268
+ }
2269
+ t(key, fallback, params) {
2270
+ return this.i18n.t(key, params, fallback, PRAXIS_CHARTS_I18N_NAMESPACE);
2238
2271
  }
2239
2272
  canOpenConfigEditor() {
2240
2273
  return this.enableCustomization() && !!this.settingsPanel && !!this.runtimeChartDocument();
@@ -2382,6 +2415,34 @@ class PraxisChartComponent {
2382
2415
  });
2383
2416
  }
2384
2417
  }
2418
+ toggleAccessibleData() {
2419
+ this.showAccessibleData.update((visible) => !visible);
2420
+ }
2421
+ activateAccessiblePoint(point) {
2422
+ this.handlePointClick({
2423
+ chartId: point.chartId,
2424
+ seriesId: point.seriesId,
2425
+ seriesName: point.seriesName,
2426
+ category: point.category,
2427
+ value: point.value,
2428
+ data: point.data,
2429
+ });
2430
+ }
2431
+ accessiblePointPressed(point) {
2432
+ if (!this.renderConfig().interactions?.selection) {
2433
+ return null;
2434
+ }
2435
+ return this.activeSelectionSignature() === this.buildPointSelectionSignature(this.renderConfig(), point)
2436
+ ? 'true'
2437
+ : 'false';
2438
+ }
2439
+ accessiblePointActionLabel(point) {
2440
+ return this.t('praxis.charts.runtime.accessibility.activatePoint', 'Activate {{ category }}, {{ series }}: {{ value }}', {
2441
+ category: point.categoryLabel,
2442
+ series: point.seriesLabel,
2443
+ value: point.valueLabel,
2444
+ });
2445
+ }
2385
2446
  buildSelectionEvent(config, event) {
2386
2447
  const action = config.interactions?.eventActions?.selectionChange;
2387
2448
  const filters = this.buildEventFilters(config, event, action?.mapping);
@@ -2751,6 +2812,22 @@ class PraxisChartComponent {
2751
2812
  ></button>
2752
2813
  }
2753
2814
 
2815
+ @if (loadState() === 'ready' && accessiblePoints().length) {
2816
+ <button
2817
+ class="praxis-chart-data-trigger"
2818
+ [class.praxis-chart-data-trigger-with-settings]="canOpenConfigEditor()"
2819
+ praxisIconButton="table_view"
2820
+ size="compact"
2821
+ appearance="filled"
2822
+ type="button"
2823
+ [attr.aria-label]="accessibleDataToggleLabel()"
2824
+ [attr.aria-controls]="accessibilityPanelId"
2825
+ [attr.aria-expanded]="showAccessibleData()"
2826
+ [matTooltip]="accessibleDataToggleLabel()"
2827
+ (click)="toggleAccessibleData()"
2828
+ ></button>
2829
+ }
2830
+
2754
2831
  @if (loadState() === 'loading') {
2755
2832
  <div class="praxis-chart-state praxis-chart-state-loading">
2756
2833
  <div class="praxis-chart-loading-hero" aria-hidden="true">
@@ -2794,10 +2871,53 @@ class PraxisChartComponent {
2794
2871
  }
2795
2872
  </div>
2796
2873
  } @else {
2797
- <div #chartHost class="praxis-chart-host"></div>
2874
+ @if (showAccessibleData()) {
2875
+ <section
2876
+ class="praxis-chart-accessible-data"
2877
+ [id]="accessibilityPanelId"
2878
+ role="region"
2879
+ [attr.aria-labelledby]="accessibilityPanelTitleId"
2880
+ >
2881
+ <h3 [id]="accessibilityPanelTitleId">{{ accessibleDataTitle() }}</h3>
2882
+ <table>
2883
+ <caption>{{ accessibleDataDescription() }}</caption>
2884
+ <thead>
2885
+ <tr>
2886
+ <th scope="col">{{ accessibleCategoryHeading() }}</th>
2887
+ <th scope="col">{{ accessibleSeriesHeading() }}</th>
2888
+ <th scope="col">{{ accessibleValueHeading() }}</th>
2889
+ </tr>
2890
+ </thead>
2891
+ <tbody>
2892
+ @for (point of accessiblePoints(); track point.id) {
2893
+ <tr>
2894
+ <th scope="row">
2895
+ <button
2896
+ type="button"
2897
+ class="praxis-chart-point-action"
2898
+ [attr.aria-label]="accessiblePointActionLabel(point)"
2899
+ [attr.aria-pressed]="accessiblePointPressed(point)"
2900
+ (click)="activateAccessiblePoint(point)"
2901
+ >
2902
+ {{ point.categoryLabel }}
2903
+ </button>
2904
+ </th>
2905
+ <td>{{ point.seriesLabel }}</td>
2906
+ <td>{{ point.valueLabel }}</td>
2907
+ </tr>
2908
+ }
2909
+ </tbody>
2910
+ </table>
2911
+ </section>
2912
+ }
2913
+ <div
2914
+ #chartHost
2915
+ class="praxis-chart-host"
2916
+ [attr.aria-hidden]="showAccessibleData() ? 'true' : null"
2917
+ ></div>
2798
2918
  }
2799
2919
  </section>
2800
- `, 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(--praxis-chart-config-text-color, var(--md-sys-color-on-surface, #1a1b20))}.praxis-chart-state-description{font-size:.925rem;color:color-mix(in srgb,var(--praxis-chart-config-text-color, var(--md-sys-color-on-surface-variant, #5a5d67)) 72%,transparent);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 });
2920
+ `, 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-data-trigger{position:absolute;top:10px;right:10px;z-index:5;--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-data-trigger-with-settings{right:50px}.praxis-chart-accessible-data{position:absolute;inset:0;z-index:4;overflow:auto;padding:52px 16px 16px;color:var(--praxis-chart-config-text-color, var(--md-sys-color-on-surface, #1a1b20));background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-surface-bg, var(--md-sys-color-surface-container-lowest, #fff)) )}.praxis-chart-accessible-data h3{margin:0 0 12px;font-size:1rem;line-height:1.4}.praxis-chart-accessible-data table{width:100%;border-collapse:collapse;font-size:.875rem}.praxis-chart-accessible-data caption{padding:0 0 12px;text-align:left;color:var(--md-sys-color-on-surface-variant, #5a5d67)}.praxis-chart-accessible-data th,.praxis-chart-accessible-data td{padding:8px;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 44%,transparent);text-align:left}.praxis-chart-accessible-data thead th{font-weight:600}.praxis-chart-point-action{appearance:none;padding:4px 6px;border:0;border-radius:4px;color:var(--md-sys-color-primary, #1263b4);background:transparent;font:inherit;font-weight:600;text-align:left;cursor:pointer}.praxis-chart-point-action:hover{background:color-mix(in srgb,var(--md-sys-color-primary, #1263b4) 10%,transparent)}.praxis-chart-point-action:focus-visible{outline:3px solid var(--md-sys-color-primary, #1263b4);outline-offset:2px}.praxis-chart-point-action[aria-pressed=true]{color:var(--md-sys-color-on-primary-container, #001b3f);background:var(--md-sys-color-primary-container, #d7e3ff)}.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(--praxis-chart-config-text-color, var(--md-sys-color-on-surface, #1a1b20))}.praxis-chart-state-description{font-size:.925rem;color:color-mix(in srgb,var(--praxis-chart-config-text-color, var(--md-sys-color-on-surface-variant, #5a5d67)) 72%,transparent);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 });
2801
2921
  }
2802
2922
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisChartComponent, decorators: [{
2803
2923
  type: Component,
@@ -2832,6 +2952,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2832
2952
  ></button>
2833
2953
  }
2834
2954
 
2955
+ @if (loadState() === 'ready' && accessiblePoints().length) {
2956
+ <button
2957
+ class="praxis-chart-data-trigger"
2958
+ [class.praxis-chart-data-trigger-with-settings]="canOpenConfigEditor()"
2959
+ praxisIconButton="table_view"
2960
+ size="compact"
2961
+ appearance="filled"
2962
+ type="button"
2963
+ [attr.aria-label]="accessibleDataToggleLabel()"
2964
+ [attr.aria-controls]="accessibilityPanelId"
2965
+ [attr.aria-expanded]="showAccessibleData()"
2966
+ [matTooltip]="accessibleDataToggleLabel()"
2967
+ (click)="toggleAccessibleData()"
2968
+ ></button>
2969
+ }
2970
+
2835
2971
  @if (loadState() === 'loading') {
2836
2972
  <div class="praxis-chart-state praxis-chart-state-loading">
2837
2973
  <div class="praxis-chart-loading-hero" aria-hidden="true">
@@ -2875,10 +3011,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2875
3011
  }
2876
3012
  </div>
2877
3013
  } @else {
2878
- <div #chartHost class="praxis-chart-host"></div>
3014
+ @if (showAccessibleData()) {
3015
+ <section
3016
+ class="praxis-chart-accessible-data"
3017
+ [id]="accessibilityPanelId"
3018
+ role="region"
3019
+ [attr.aria-labelledby]="accessibilityPanelTitleId"
3020
+ >
3021
+ <h3 [id]="accessibilityPanelTitleId">{{ accessibleDataTitle() }}</h3>
3022
+ <table>
3023
+ <caption>{{ accessibleDataDescription() }}</caption>
3024
+ <thead>
3025
+ <tr>
3026
+ <th scope="col">{{ accessibleCategoryHeading() }}</th>
3027
+ <th scope="col">{{ accessibleSeriesHeading() }}</th>
3028
+ <th scope="col">{{ accessibleValueHeading() }}</th>
3029
+ </tr>
3030
+ </thead>
3031
+ <tbody>
3032
+ @for (point of accessiblePoints(); track point.id) {
3033
+ <tr>
3034
+ <th scope="row">
3035
+ <button
3036
+ type="button"
3037
+ class="praxis-chart-point-action"
3038
+ [attr.aria-label]="accessiblePointActionLabel(point)"
3039
+ [attr.aria-pressed]="accessiblePointPressed(point)"
3040
+ (click)="activateAccessiblePoint(point)"
3041
+ >
3042
+ {{ point.categoryLabel }}
3043
+ </button>
3044
+ </th>
3045
+ <td>{{ point.seriesLabel }}</td>
3046
+ <td>{{ point.valueLabel }}</td>
3047
+ </tr>
3048
+ }
3049
+ </tbody>
3050
+ </table>
3051
+ </section>
3052
+ }
3053
+ <div
3054
+ #chartHost
3055
+ class="praxis-chart-host"
3056
+ [attr.aria-hidden]="showAccessibleData() ? 'true' : null"
3057
+ ></div>
2879
3058
  }
2880
3059
  </section>
2881
- `, 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(--praxis-chart-config-text-color, var(--md-sys-color-on-surface, #1a1b20))}.praxis-chart-state-description{font-size:.925rem;color:color-mix(in srgb,var(--praxis-chart-config-text-color, var(--md-sys-color-on-surface-variant, #5a5d67)) 72%,transparent);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"] }]
3060
+ `, 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-data-trigger{position:absolute;top:10px;right:10px;z-index:5;--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-data-trigger-with-settings{right:50px}.praxis-chart-accessible-data{position:absolute;inset:0;z-index:4;overflow:auto;padding:52px 16px 16px;color:var(--praxis-chart-config-text-color, var(--md-sys-color-on-surface, #1a1b20));background:var( --praxis-chart-config-surface-bg, var(--praxis-chart-surface-bg, var(--md-sys-color-surface-container-lowest, #fff)) )}.praxis-chart-accessible-data h3{margin:0 0 12px;font-size:1rem;line-height:1.4}.praxis-chart-accessible-data table{width:100%;border-collapse:collapse;font-size:.875rem}.praxis-chart-accessible-data caption{padding:0 0 12px;text-align:left;color:var(--md-sys-color-on-surface-variant, #5a5d67)}.praxis-chart-accessible-data th,.praxis-chart-accessible-data td{padding:8px;border-bottom:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 44%,transparent);text-align:left}.praxis-chart-accessible-data thead th{font-weight:600}.praxis-chart-point-action{appearance:none;padding:4px 6px;border:0;border-radius:4px;color:var(--md-sys-color-primary, #1263b4);background:transparent;font:inherit;font-weight:600;text-align:left;cursor:pointer}.praxis-chart-point-action:hover{background:color-mix(in srgb,var(--md-sys-color-primary, #1263b4) 10%,transparent)}.praxis-chart-point-action:focus-visible{outline:3px solid var(--md-sys-color-primary, #1263b4);outline-offset:2px}.praxis-chart-point-action[aria-pressed=true]{color:var(--md-sys-color-on-primary-container, #001b3f);background:var(--md-sys-color-primary-container, #d7e3ff)}.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(--praxis-chart-config-text-color, var(--md-sys-color-on-surface, #1a1b20))}.praxis-chart-state-description{font-size:.925rem;color:color-mix(in srgb,var(--praxis-chart-config-text-color, var(--md-sys-color-on-surface-variant, #5a5d67)) 72%,transparent);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"] }]
2882
3061
  }], 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 }] }] } });
2883
3062
  function mergeRemoteQueryContext(config, queryContext, runtimeFilters) {
2884
3063
  const dataSource = config.dataSource;
@@ -2923,6 +3102,111 @@ function resolveUnsupportedStatsFilterExpressionDiagnostic(queryContext) {
2923
3102
  : '';
2924
3103
  return `Praxis Charts received a governed queryContext.filterExpression, but praxis.stats execution currently accepts only flat statsRequest.filter criteria. The expression was rejected instead of being flattened into an unsafe AND filter.${decision}${reason}`;
2925
3104
  }
3105
+ function buildAccessibleChartPoints(config, transformed, formatValue, pointFallback, seriesFallback) {
3106
+ if (transformed.mode === 'gauge') {
3107
+ const gauge = transformed.gauge;
3108
+ if (!gauge) {
3109
+ return [];
3110
+ }
3111
+ const series = config.series[0];
3112
+ return [createAccessiblePoint({
3113
+ id: gauge.id,
3114
+ config,
3115
+ seriesId: series?.id,
3116
+ seriesName: series?.name ?? series?.metric?.label,
3117
+ category: gauge.name,
3118
+ value: gauge.value,
3119
+ data: gauge.data,
3120
+ index: 0,
3121
+ formatValue,
3122
+ pointFallback,
3123
+ seriesFallback,
3124
+ })];
3125
+ }
3126
+ if (transformed.mode === 'pie' || transformed.mode === 'funnel' || transformed.mode === 'treemap') {
3127
+ const series = config.series[0];
3128
+ return transformed.slices.map((slice, index) => createAccessiblePoint({
3129
+ id: slice.id,
3130
+ config,
3131
+ seriesId: series?.id,
3132
+ seriesName: series?.name ?? series?.metric?.label,
3133
+ category: slice.name,
3134
+ value: slice.value,
3135
+ data: slice.data,
3136
+ index,
3137
+ formatValue,
3138
+ pointFallback,
3139
+ seriesFallback,
3140
+ }));
3141
+ }
3142
+ return transformed.series.flatMap((series) => series.points.flatMap((point, index) => {
3143
+ const value = accessiblePointValue(point);
3144
+ if (value === null || value === undefined) {
3145
+ return [];
3146
+ }
3147
+ const data = accessiblePointData(point, transformed.categories[index], value);
3148
+ const category = transformed.categories[index]
3149
+ ?? accessibleCategoryFromData(config, data)
3150
+ ?? pointFallback(index);
3151
+ return [createAccessiblePoint({
3152
+ id: `${series.id}:${index}:${category}`,
3153
+ config,
3154
+ seriesId: series.id,
3155
+ seriesName: series.name,
3156
+ category,
3157
+ value,
3158
+ data,
3159
+ index,
3160
+ formatValue,
3161
+ pointFallback,
3162
+ seriesFallback,
3163
+ })];
3164
+ }));
3165
+ }
3166
+ function createAccessiblePoint(options) {
3167
+ const categoryLabel = options.category?.trim() || options.pointFallback(options.index);
3168
+ const seriesLabel = options.seriesName?.trim()
3169
+ || options.seriesId?.trim()
3170
+ || options.seriesFallback();
3171
+ return {
3172
+ id: options.id,
3173
+ chartId: options.config.id,
3174
+ seriesId: options.seriesId,
3175
+ seriesName: options.seriesName,
3176
+ category: options.category,
3177
+ value: options.value,
3178
+ data: options.data ?? {
3179
+ category: options.category,
3180
+ value: options.value,
3181
+ },
3182
+ categoryLabel,
3183
+ seriesLabel,
3184
+ valueLabel: options.formatValue(options.value),
3185
+ };
3186
+ }
3187
+ function accessiblePointValue(point) {
3188
+ if (Array.isArray(point)) {
3189
+ return point.length > 1 ? point[1] : point[0];
3190
+ }
3191
+ if (point && typeof point === 'object' && 'value' in point) {
3192
+ return accessiblePointValue(point['value']);
3193
+ }
3194
+ return point;
3195
+ }
3196
+ function accessiblePointData(point, category, value) {
3197
+ if (point && typeof point === 'object' && !Array.isArray(point)) {
3198
+ return point;
3199
+ }
3200
+ return { category, value };
3201
+ }
3202
+ function accessibleCategoryFromData(config, data) {
3203
+ const categoryField = config.axes?.x?.field
3204
+ ?? config.series.find((series) => series.categoryField)?.categoryField;
3205
+ const value = categoryField ? data[categoryField] : data['category'];
3206
+ return value === null || value === undefined || value === ''
3207
+ ? undefined
3208
+ : String(value);
3209
+ }
2926
3210
  function normalizeRemoteDataResolverResult$1(result) {
2927
3211
  if (Array.isArray(result)) {
2928
3212
  return of(result);
@@ -4427,11 +4711,13 @@ const CARTESIAN_GRID_BOTTOM_WITHOUT_LEGEND = 40;
4427
4711
  const DEFAULT_VALUE_LOCALE = 'pt-BR';
4428
4712
  class EChartsOptionBuilderService {
4429
4713
  transformer;
4714
+ i18n = inject(PraxisI18nService);
4430
4715
  constructor(transformer) {
4431
4716
  this.transformer = transformer;
4432
4717
  }
4433
4718
  build(config, rows) {
4434
4719
  const transformed = this.transformer.transform(config, rows);
4720
+ const accessibility = this.buildAccessibility(config, transformed);
4435
4721
  const palette = config.theme?.palette?.length ? config.theme.palette : PRAXIS_CHART_DEFAULT_PALETTE;
4436
4722
  const tooltipEnabled = config.theme?.tooltip?.enabled ?? true;
4437
4723
  const legendVisible = config.theme?.legend?.visible ?? true;
@@ -4501,6 +4787,7 @@ class EChartsOptionBuilderService {
4501
4787
  }],
4502
4788
  };
4503
4789
  return {
4790
+ aria: accessibility,
4504
4791
  backgroundColor: config.theme?.backgroundColor,
4505
4792
  color: palette,
4506
4793
  title: this.buildTitle(config, 'center', textColor),
@@ -4545,6 +4832,7 @@ class EChartsOptionBuilderService {
4545
4832
  })),
4546
4833
  };
4547
4834
  return {
4835
+ aria: accessibility,
4548
4836
  backgroundColor: config.theme?.backgroundColor,
4549
4837
  color: palette,
4550
4838
  title: this.buildTitle(config, 'center', textColor),
@@ -4595,6 +4883,7 @@ class EChartsOptionBuilderService {
4595
4883
  })),
4596
4884
  };
4597
4885
  return {
4886
+ aria: accessibility,
4598
4887
  backgroundColor: config.theme?.backgroundColor,
4599
4888
  color: palette,
4600
4889
  title: this.buildTitle(config, 'center', textColor),
@@ -4638,6 +4927,7 @@ class EChartsOptionBuilderService {
4638
4927
  })),
4639
4928
  };
4640
4929
  return {
4930
+ aria: accessibility,
4641
4931
  backgroundColor: config.theme?.backgroundColor,
4642
4932
  color: palette,
4643
4933
  title: this.buildTitle(config, 'center', textColor),
@@ -4667,6 +4957,7 @@ class EChartsOptionBuilderService {
4667
4957
  data: series.points,
4668
4958
  }));
4669
4959
  return {
4960
+ aria: accessibility,
4670
4961
  backgroundColor: config.theme?.backgroundColor,
4671
4962
  color: palette,
4672
4963
  title: this.buildTitle(config, 'left', textColor),
@@ -4713,6 +5004,7 @@ class EChartsOptionBuilderService {
4713
5004
  ? 'time'
4714
5005
  : config.axes?.x?.type ?? 'category';
4715
5006
  return {
5007
+ aria: accessibility,
4716
5008
  backgroundColor: config.theme?.backgroundColor,
4717
5009
  color: palette,
4718
5010
  title: this.buildTitle(config, 'left', textColor),
@@ -4799,6 +5091,57 @@ class EChartsOptionBuilderService {
4799
5091
  }
4800
5092
  return undefined;
4801
5093
  }
5094
+ buildAccessibility(config, transformed) {
5095
+ const title = this.i18n.resolve(config.title).trim();
5096
+ const seriesCount = transformed.mode === 'gauge'
5097
+ ? transformed.gauge ? 1 : 0
5098
+ : transformed.slices.length
5099
+ ? 1
5100
+ : transformed.series.length;
5101
+ const pointCount = transformed.mode === 'gauge'
5102
+ ? transformed.gauge ? 1 : 0
5103
+ : transformed.slices.length
5104
+ ? transformed.slices.length
5105
+ : transformed.series.reduce((total, series) => total + series.points.length, 0);
5106
+ const params = {
5107
+ title,
5108
+ type: this.chartTypeLabel(config.type),
5109
+ seriesCount,
5110
+ pointCount,
5111
+ };
5112
+ const description = title
5113
+ ? this.t('praxis.charts.runtime.accessibility.ariaDescriptionWithTitle', 'Chart "{{ title }}". Type: {{ type }}. Series: {{ seriesCount }}. Points: {{ pointCount }}. Complete values are available under "Show chart data".', params)
5114
+ : this.t('praxis.charts.runtime.accessibility.ariaDescriptionWithoutTitle', 'Chart. Type: {{ type }}. Series: {{ seriesCount }}. Points: {{ pointCount }}. Complete values are available under "Show chart data".', params);
5115
+ return {
5116
+ enabled: true,
5117
+ label: {
5118
+ enabled: true,
5119
+ description,
5120
+ },
5121
+ };
5122
+ }
5123
+ chartTypeLabel(type) {
5124
+ const fallbacks = {
5125
+ bar: 'Bar',
5126
+ 'horizontal-bar': 'Horizontal bar',
5127
+ line: 'Line',
5128
+ area: 'Area',
5129
+ 'stacked-bar': 'Stacked bar',
5130
+ 'stacked-area': 'Stacked area',
5131
+ combo: 'Combination',
5132
+ pie: 'Pie',
5133
+ donut: 'Donut',
5134
+ scatter: 'Scatter',
5135
+ funnel: 'Funnel',
5136
+ pyramid: 'Pyramid',
5137
+ treemap: 'Treemap',
5138
+ gauge: 'Gauge',
5139
+ };
5140
+ return this.t(`praxis.charts.runtime.accessibility.chartType.${type}`, fallbacks[type]);
5141
+ }
5142
+ t(key, fallback, params) {
5143
+ return this.i18n.t(key, params, fallback, PRAXIS_CHARTS_I18N_NAMESPACE);
5144
+ }
4802
5145
  resolveTextColor(config) {
4803
5146
  return config.theme?.textColor ?? '#4b5563';
4804
5147
  }
@@ -5458,6 +5801,33 @@ const PRAXIS_CHARTS_EN_US = {
5458
5801
  'praxis.charts.runtime.gaugeValueInvalid': 'The gauge requires one finite metric value.',
5459
5802
  'praxis.charts.runtime.gaugeValueOutOfRange': 'The gauge value is outside the configured scale. Review the metric and scale instead of clamping the value.',
5460
5803
  'praxis.charts.runtime.booleanFalseLabel': 'Not {{ label }}',
5804
+ 'praxis.charts.runtime.accessibility.showData': 'Show chart data',
5805
+ 'praxis.charts.runtime.accessibility.hideData': 'Hide chart data',
5806
+ 'praxis.charts.runtime.accessibility.dataTitle': 'Chart data',
5807
+ 'praxis.charts.runtime.accessibility.dataDescription': 'Structured alternative for the values represented in the chart.',
5808
+ 'praxis.charts.runtime.accessibility.category': 'Category',
5809
+ 'praxis.charts.runtime.accessibility.series': 'Series',
5810
+ 'praxis.charts.runtime.accessibility.value': 'Value',
5811
+ 'praxis.charts.runtime.accessibility.pointFallback': 'Point {{ index }}',
5812
+ 'praxis.charts.runtime.accessibility.seriesFallback': 'Series',
5813
+ 'praxis.charts.runtime.accessibility.notAvailable': 'Not available',
5814
+ 'praxis.charts.runtime.accessibility.activatePoint': 'Activate {{ category }}, {{ series }}: {{ value }}',
5815
+ 'praxis.charts.runtime.accessibility.ariaDescriptionWithTitle': 'Chart "{{ title }}". Type: {{ type }}. Series: {{ seriesCount }}. Points: {{ pointCount }}. Complete values are available under "Show chart data".',
5816
+ 'praxis.charts.runtime.accessibility.ariaDescriptionWithoutTitle': 'Chart. Type: {{ type }}. Series: {{ seriesCount }}. Points: {{ pointCount }}. Complete values are available under "Show chart data".',
5817
+ 'praxis.charts.runtime.accessibility.chartType.bar': 'Bar',
5818
+ 'praxis.charts.runtime.accessibility.chartType.horizontal-bar': 'Horizontal bar',
5819
+ 'praxis.charts.runtime.accessibility.chartType.line': 'Line',
5820
+ 'praxis.charts.runtime.accessibility.chartType.area': 'Area',
5821
+ 'praxis.charts.runtime.accessibility.chartType.stacked-bar': 'Stacked bar',
5822
+ 'praxis.charts.runtime.accessibility.chartType.stacked-area': 'Stacked area',
5823
+ 'praxis.charts.runtime.accessibility.chartType.combo': 'Combination',
5824
+ 'praxis.charts.runtime.accessibility.chartType.pie': 'Pie',
5825
+ 'praxis.charts.runtime.accessibility.chartType.donut': 'Donut',
5826
+ 'praxis.charts.runtime.accessibility.chartType.scatter': 'Scatter',
5827
+ 'praxis.charts.runtime.accessibility.chartType.funnel': 'Funnel',
5828
+ 'praxis.charts.runtime.accessibility.chartType.pyramid': 'Pyramid',
5829
+ 'praxis.charts.runtime.accessibility.chartType.treemap': 'Treemap',
5830
+ 'praxis.charts.runtime.accessibility.chartType.gauge': 'Gauge',
5461
5831
  'praxis.charts.runtime.comparisonCurrent': 'Current',
5462
5832
  'praxis.charts.runtime.comparisonPrevious': 'Previous',
5463
5833
  'praxis.charts.widget.missingDocument': 'This widget does not have a canonical chart document yet. Runtime inputs are preserved until a canonical chartDocument is provided by the host.',
@@ -5693,6 +6063,33 @@ const PRAXIS_CHARTS_PT_BR = {
5693
6063
  'praxis.charts.runtime.gaugeValueInvalid': 'O velocímetro exige um único valor numérico finito.',
5694
6064
  'praxis.charts.runtime.gaugeValueOutOfRange': 'O valor do velocímetro está fora da escala configurada. Revise a métrica e a escala em vez de limitar o valor silenciosamente.',
5695
6065
  'praxis.charts.runtime.booleanFalseLabel': 'Não {{ label }}',
6066
+ 'praxis.charts.runtime.accessibility.showData': 'Exibir dados do gráfico',
6067
+ 'praxis.charts.runtime.accessibility.hideData': 'Ocultar dados do gráfico',
6068
+ 'praxis.charts.runtime.accessibility.dataTitle': 'Dados do gráfico',
6069
+ 'praxis.charts.runtime.accessibility.dataDescription': 'Alternativa estruturada para os valores representados no gráfico.',
6070
+ 'praxis.charts.runtime.accessibility.category': 'Categoria',
6071
+ 'praxis.charts.runtime.accessibility.series': 'Série',
6072
+ 'praxis.charts.runtime.accessibility.value': 'Valor',
6073
+ 'praxis.charts.runtime.accessibility.pointFallback': 'Ponto {{ index }}',
6074
+ 'praxis.charts.runtime.accessibility.seriesFallback': 'Série',
6075
+ 'praxis.charts.runtime.accessibility.notAvailable': 'Não disponível',
6076
+ 'praxis.charts.runtime.accessibility.activatePoint': 'Ativar {{ category }}, {{ series }}: {{ value }}',
6077
+ 'praxis.charts.runtime.accessibility.ariaDescriptionWithTitle': 'Gráfico "{{ title }}". Tipo: {{ type }}. Séries: {{ seriesCount }}. Pontos: {{ pointCount }}. Os valores completos estão disponíveis em "Exibir dados do gráfico".',
6078
+ 'praxis.charts.runtime.accessibility.ariaDescriptionWithoutTitle': 'Gráfico. Tipo: {{ type }}. Séries: {{ seriesCount }}. Pontos: {{ pointCount }}. Os valores completos estão disponíveis em "Exibir dados do gráfico".',
6079
+ 'praxis.charts.runtime.accessibility.chartType.bar': 'Barras',
6080
+ 'praxis.charts.runtime.accessibility.chartType.horizontal-bar': 'Barras horizontais',
6081
+ 'praxis.charts.runtime.accessibility.chartType.line': 'Linhas',
6082
+ 'praxis.charts.runtime.accessibility.chartType.area': 'Área',
6083
+ 'praxis.charts.runtime.accessibility.chartType.stacked-bar': 'Barras empilhadas',
6084
+ 'praxis.charts.runtime.accessibility.chartType.stacked-area': 'Área empilhada',
6085
+ 'praxis.charts.runtime.accessibility.chartType.combo': 'Combinado',
6086
+ 'praxis.charts.runtime.accessibility.chartType.pie': 'Pizza',
6087
+ 'praxis.charts.runtime.accessibility.chartType.donut': 'Rosca',
6088
+ 'praxis.charts.runtime.accessibility.chartType.scatter': 'Dispersão',
6089
+ 'praxis.charts.runtime.accessibility.chartType.funnel': 'Funil',
6090
+ 'praxis.charts.runtime.accessibility.chartType.pyramid': 'Pirâmide',
6091
+ 'praxis.charts.runtime.accessibility.chartType.treemap': 'Mapa de árvore',
6092
+ 'praxis.charts.runtime.accessibility.chartType.gauge': 'Velocímetro',
5696
6093
  'praxis.charts.runtime.comparisonCurrent': 'Atual',
5697
6094
  'praxis.charts.runtime.comparisonPrevious': 'Anterior',
5698
6095
  'praxis.charts.widget.missingDocument': 'Este widget ainda não possui um documento canônico de gráfico. Os inputs runtime são preservados até o host fornecer um chartDocument canônico.',
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@praxisui/charts",
3
- "version": "9.0.38",
3
+ "version": "9.0.39",
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.38",
8
+ "@praxisui/core": "^9.0.39",
9
9
  "@angular/forms": "^21.0.0",
10
10
  "@angular/material": "^21.0.0",
11
- "@praxisui/table": "^9.0.38",
11
+ "@praxisui/table": "^9.0.39",
12
12
  "rxjs": "~7.8.0"
13
13
  },
14
14
  "dependencies": {
@@ -547,6 +547,12 @@ interface ChartEditorTargetOption {
547
547
  events?: ChartEditorEventKey[];
548
548
  }
549
549
 
550
+ interface PraxisChartAccessiblePoint extends PraxisChartPointEvent {
551
+ id: string;
552
+ categoryLabel: string;
553
+ seriesLabel: string;
554
+ valueLabel: string;
555
+ }
550
556
  declare class PraxisChartComponent {
551
557
  readonly config: _angular_core.InputSignal<PraxisChartConfig>;
552
558
  readonly data: _angular_core.InputSignal<PraxisChartDataRow[] | null>;
@@ -592,6 +598,9 @@ declare class PraxisChartComponent {
592
598
  private readonly mappedRuntimeConfig;
593
599
  private readonly chartDocumentMappingError;
594
600
  private readonly activeSelectionSignature;
601
+ readonly showAccessibleData: _angular_core.WritableSignal<boolean>;
602
+ readonly accessibilityPanelId: string;
603
+ readonly accessibilityPanelTitleId: string;
595
604
  private readonly fillContainerHeight;
596
605
  private readonly renderAttempt;
597
606
  private resizeFrameId;
@@ -620,16 +629,24 @@ declare class PraxisChartComponent {
620
629
  readonly surfaceTextColor: _angular_core.Signal<string | null>;
621
630
  readonly renderConfig: _angular_core.Signal<PraxisChartConfig>;
622
631
  readonly loadingLabel: _angular_core.Signal<string>;
623
- readonly transformedData: _angular_core.Signal<_praxisui_charts.PraxisChartTransformedData>;
632
+ readonly transformedData: _angular_core.Signal<PraxisChartTransformedData>;
633
+ readonly accessiblePoints: _angular_core.Signal<PraxisChartAccessiblePoint[]>;
624
634
  readonly dataDiagnostic: _angular_core.Signal<PraxisChartDataDiagnostic | null>;
625
635
  readonly emptyTitle: _angular_core.Signal<string>;
626
636
  readonly emptyDescription: _angular_core.Signal<string>;
627
637
  readonly errorTitle: _angular_core.Signal<string>;
628
638
  readonly errorDescription: _angular_core.Signal<string>;
629
639
  readonly editChartLabel: _angular_core.Signal<string>;
640
+ readonly accessibleDataToggleLabel: _angular_core.Signal<string>;
641
+ readonly accessibleDataTitle: _angular_core.Signal<string>;
642
+ readonly accessibleDataDescription: _angular_core.Signal<string>;
643
+ readonly accessibleCategoryHeading: _angular_core.Signal<string>;
644
+ readonly accessibleSeriesHeading: _angular_core.Signal<string>;
645
+ readonly accessibleValueHeading: _angular_core.Signal<string>;
630
646
  readonly loadState: _angular_core.Signal<PraxisChartLoadState>;
631
647
  constructor();
632
648
  private resolveDataDiagnosticDescription;
649
+ private formatAccessibleValue;
633
650
  private t;
634
651
  canOpenConfigEditor(): boolean;
635
652
  openConfigEditor(): Promise<void>;
@@ -638,6 +655,10 @@ declare class PraxisChartComponent {
638
655
  private isConfigEditorContextResult;
639
656
  private asRecord;
640
657
  private handlePointClick;
658
+ toggleAccessibleData(): void;
659
+ activateAccessiblePoint(point: PraxisChartAccessiblePoint): void;
660
+ accessiblePointPressed(point: PraxisChartAccessiblePoint): string | null;
661
+ accessiblePointActionLabel(point: PraxisChartAccessiblePoint): string;
641
662
  private buildSelectionEvent;
642
663
  private buildPointSelectionSignature;
643
664
  private buildSelectionScopeSignature;