@acorex/charts 22.1.8 → 22.2.0-next.1

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.
@@ -661,16 +661,57 @@ class AXLineChartComponent extends AXChartComponent {
661
661
  yMin = dataYMin;
662
662
  yMax = dataYMax;
663
663
  }
664
- const yRange = yMax - yMin || 1;
665
- this.yScale = this.d3
666
- .scaleLinear()
667
- .domain([yMin, yMax + yRange * paddingMultiplier])
668
- .nice()
669
- .range([this.height, 0]);
664
+ const ySpan = yMax - yMin;
665
+ const integralDomain = allDataPoints.every((point) => Number.isFinite(point.y) && Number.isInteger(point.y)) &&
666
+ Number.isInteger(yMin) &&
667
+ Number.isInteger(yMax);
668
+ // A 5% pad on 0–1 becomes 1.05, and d3.nice() then stretches that to 1.1 with 0.1 ticks.
669
+ if (integralDomain && ySpan <= 8) {
670
+ const upper = ySpan === 0 ? yMax + 1 : yMax;
671
+ const inset = Math.min(this.wholeNumberRangeInset(), Math.floor(this.height / 4));
672
+ this.yScale = this.d3
673
+ .scaleLinear()
674
+ .domain([yMin, upper])
675
+ .range([Math.max(inset, this.height - inset), inset]);
676
+ return;
677
+ }
678
+ // A zero span used to become `1 * axisPadding%` (0.05). D3 then drew 0.005 ticks for an all-zero series.
679
+ const domainMax = ySpan === 0 ? yMax + 1 : yMax + ySpan * paddingMultiplier;
680
+ this.yScale = this.d3.scaleLinear().domain([yMin, domainMax]).nice().range([this.height, 0]);
670
681
  if (yAxisStartsAtZero && dataYMin >= 0) {
671
- this.yScale.domain([0, this.yScale.domain()[1]]);
682
+ const upper = dataYMax === 0 ? Math.max(1, this.yScale.domain()[1]) : this.yScale.domain()[1];
683
+ this.yScale.domain([0, upper]);
684
+ }
685
+ if (integralDomain) {
686
+ const [niceMin, niceMax] = this.yScale.domain();
687
+ const snappedMin = Math.floor(niceMin);
688
+ const snappedMax = Math.max(snappedMin + 1, Math.ceil(niceMax));
689
+ this.yScale.domain([snappedMin, snappedMax]);
672
690
  }
673
691
  }
692
+ /** Keeps a point that sits on the domain edge inside the clipped SVG. */
693
+ wholeNumberRangeInset() {
694
+ const options = this.effectiveOptions();
695
+ const pointRadius = options.showPoints === false ? 0 : (options.pointRadius ?? this.DEFAULT_POINT_RADIUS);
696
+ const stroke = (options.lineWidth ?? this.DEFAULT_LINE_WIDTH) / 2;
697
+ return Math.ceil(Math.max(pointRadius, stroke) + 1);
698
+ }
699
+ /** Integer ticks for a short whole-number domain so D3 does not subdivide 0–1 into decimals. */
700
+ resolveYAxisTickValues() {
701
+ const [min, max] = this.yScale.domain();
702
+ if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min)
703
+ return null;
704
+ if (!Number.isInteger(min) || !Number.isInteger(max))
705
+ return null;
706
+ const span = max - min;
707
+ if (span > 8)
708
+ return null;
709
+ const values = [];
710
+ for (let value = min; value <= max; value += 1) {
711
+ values.push(value);
712
+ }
713
+ return values;
714
+ }
674
715
  createAxes(options) {
675
716
  const showXAxis = options.showXAxis !== false;
676
717
  const showYAxis = options.showYAxis !== false;
@@ -766,6 +807,7 @@ class AXLineChartComponent extends AXChartComponent {
766
807
  this.createXAxisTitle(axesGroup, options.xAxisLabel, xTickAreaHeight, dynamicXAxisTickFontSize);
767
808
  }
768
809
  }
810
+ const yTickValues = this.resolveYAxisTickValues();
769
811
  if (showYAxis) {
770
812
  // Create Y axis with smart number formatting
771
813
  const yAxisGenerator = this.d3
@@ -776,13 +818,18 @@ class AXLineChartComponent extends AXChartComponent {
776
818
  const numValue = typeof value === 'number' ? value : value.valueOf();
777
819
  return formatLargeNumber(numValue);
778
820
  });
779
- // Reduce tick count for better readability with large numbers
780
- const maxValue = this.yScale.domain()[1];
781
- if (maxValue > 1e6) {
782
- yAxisGenerator.ticks(6); // Fewer ticks for millions+
821
+ if (yTickValues) {
822
+ yAxisGenerator.tickValues(yTickValues);
783
823
  }
784
- else if (maxValue > 1e3) {
785
- yAxisGenerator.ticks(8);
824
+ else {
825
+ // Reduce tick count for better readability with large numbers
826
+ const maxValue = this.yScale.domain()[1];
827
+ if (maxValue > 1e6) {
828
+ yAxisGenerator.ticks(6); // Fewer ticks for millions+
829
+ }
830
+ else if (maxValue > 1e3) {
831
+ yAxisGenerator.ticks(8);
832
+ }
786
833
  }
787
834
  this.yAxis = axesGroup.append('g').attr('class', 'ax-line-chart-axis-y').call(yAxisGenerator);
788
835
  // Style the y-axis path and lines
@@ -822,7 +869,7 @@ class AXLineChartComponent extends AXChartComponent {
822
869
  .axisLeft(this.yScale)
823
870
  .tickSize(-this.width)
824
871
  .tickFormat(() => '')
825
- .tickValues(this.yScale.ticks()));
872
+ .tickValues(yTickValues ?? this.yScale.ticks()));
826
873
  // Style the grid path and lines
827
874
  yGrid.selectAll('path').attr('stroke-width', '0');
828
875
  yGrid
@@ -1255,10 +1302,10 @@ class AXLineChartComponent extends AXChartComponent {
1255
1302
  this.showCrosshairLines(overlappingPoints[0].point);
1256
1303
  }
1257
1304
  }
1258
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1259
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.8", type: AXLineChartComponent, isStandalone: true, selector: "ax-line-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointClick: "pointClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["@layer properties;@layer components{.ax-chart-empty{pointer-events:none;position:absolute;inset:calc(var(--spacing, .25rem) * 0);z-index:1;box-sizing:border-box;display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 4);color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__card{box-sizing:border-box;max-width:300px;border-radius:var(--radius-lg, .5rem);padding:calc(var(--spacing, .25rem) * 6);text-align:center;width:80%;border:1px solid rgba(var(--ax-sys-color-border-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__icon{margin-bottom:calc(var(--spacing, .25rem) * 3);opacity:60%}.ax-chart-empty__title{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__help{font-size:.8rem;opacity:60%;color:rgba(var(--ax-sys-color-on-lightest-surface))}}@layer components{ax-line-chart{box-sizing:border-box;display:block;height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: 0, 0, 0, 0;--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{position:relative;box-sizing:border-box;height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;overflow:hidden;border-radius:var(--radius-lg, .5rem);padding:clamp(.25rem,.6vw,.5rem);contain:layout style;color:rgba(var(--ax-comp-line-chart-text-color));background-color:rgba(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{display:block;height:100%;max-height:100%;width:100%;max-width:100%;flex-shrink:0;overflow:hidden}ax-line-chart .ax-line-chart svg g:has(text){font-family:inherit}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], encapsulation: i0.ViewEncapsulation.None });
1305
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1306
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.6", type: AXLineChartComponent, isStandalone: true, selector: "ax-line-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointClick: "pointClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["@layer properties;@layer components{.ax-chart-empty{pointer-events:none;position:absolute;inset:calc(var(--spacing, .25rem) * 0);z-index:1;box-sizing:border-box;display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 4);color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__card{box-sizing:border-box;max-width:300px;border-radius:var(--radius-lg, .5rem);padding:calc(var(--spacing, .25rem) * 6);text-align:center;width:80%;border:1px solid rgba(var(--ax-sys-color-border-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__icon{margin-bottom:calc(var(--spacing, .25rem) * 3);opacity:60%}.ax-chart-empty__title{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__help{font-size:.8rem;opacity:60%;color:rgba(var(--ax-sys-color-on-lightest-surface))}}@layer components{ax-line-chart{box-sizing:border-box;display:block;height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: 0, 0, 0, 0;--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{position:relative;box-sizing:border-box;height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;overflow:hidden;border-radius:var(--radius-lg, .5rem);padding:clamp(.25rem,.6vw,.5rem);contain:layout style;color:rgba(var(--ax-comp-line-chart-text-color));background-color:rgba(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{display:block;height:100%;max-height:100%;width:100%;max-width:100%;flex-shrink:0;overflow:hidden}ax-line-chart .ax-line-chart svg g:has(text){font-family:inherit}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], encapsulation: i0.ViewEncapsulation.None });
1260
1307
  }
1261
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXLineChartComponent, decorators: [{
1308
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AXLineChartComponent, decorators: [{
1262
1309
  type: Component,
1263
1310
  args: [{ selector: 'ax-line-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["@layer properties;@layer components{.ax-chart-empty{pointer-events:none;position:absolute;inset:calc(var(--spacing, .25rem) * 0);z-index:1;box-sizing:border-box;display:flex;align-items:center;justify-content:center;padding:calc(var(--spacing, .25rem) * 4);color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__card{box-sizing:border-box;max-width:300px;border-radius:var(--radius-lg, .5rem);padding:calc(var(--spacing, .25rem) * 6);text-align:center;width:80%;border:1px solid rgba(var(--ax-sys-color-border-surface));background-color:rgba(var(--ax-sys-color-lightest-surface));color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__icon{margin-bottom:calc(var(--spacing, .25rem) * 3);opacity:60%}.ax-chart-empty__title{margin-bottom:calc(var(--spacing, .25rem) * 2);font-size:var(--text-base, 1rem);line-height:var(--tw-leading, var(--text-base--line-height, 1.5 ));--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:rgba(var(--ax-sys-color-on-lightest-surface))}.ax-chart-empty__help{font-size:.8rem;opacity:60%;color:rgba(var(--ax-sys-color-on-lightest-surface))}}@layer components{ax-line-chart{box-sizing:border-box;display:block;height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: 0, 0, 0, 0;--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{position:relative;box-sizing:border-box;height:100%;min-height:calc(var(--spacing, .25rem) * 0);width:100%;overflow:hidden;border-radius:var(--radius-lg, .5rem);padding:clamp(.25rem,.6vw,.5rem);contain:layout style;color:rgba(var(--ax-comp-line-chart-text-color));background-color:rgba(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{display:block;height:100%;max-height:100%;width:100%;max-width:100%;flex-shrink:0;overflow:hidden}ax-line-chart .ax-line-chart svg g:has(text){font-family:inherit}}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
1264
1311
  }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], pointClick: [{ type: i0.Output, args: ["pointClick"] }], chartContainerEl: [{ type: i0.ViewChild, args: ['chartContainer', { isSignal: true }] }] } });