@coreusa/final-barline 0.1.0 → 0.1.5

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Coreus
3
+ Copyright (c) 2026 @coreusa
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,12 +1,16 @@
1
1
  # Final Barline
2
2
 
3
+ <img width="764" height="209" alt="image" src="https://github.com/user-attachments/assets/d2e7626f-95d9-4579-89eb-e8747d8008ab" />
4
+
3
5
  A high-performance SVG Chart library for Svelte 5. Supports bar and line type of graphs with option to export the graph and a wide support for customization. Responsive by default.
4
6
 
7
+ **NOTE**: Final Barline is in early development 🛠️. Please file [issues](https://github.com/Coreusa/final-barline/issues) and we'll get to them as soon as we can.
8
+
5
9
  <img width="630" height="496" alt="image" src="https://github.com/user-attachments/assets/ffe674c0-9fbc-4799-87ec-9bf275faa361" />
6
10
 
7
11
  <img width="630" height="496" alt="image" src="https://github.com/user-attachments/assets/2d67a102-5b6b-42f7-a49d-f5fcac708da7" />
8
12
 
9
- ## General use
13
+ ## Get started
10
14
 
11
15
  ```sh
12
16
  # install Final Barline
@@ -20,9 +24,11 @@ npm install @coreusa/final-barline
20
24
  yarn add @coreusa/final-barline
21
25
  ```
22
26
 
27
+ ## General use
28
+
23
29
  ```sh
24
30
  # import the component
25
- import Barline from '@coreusa/final-barline';
31
+ import { Barline } from '@coreusa/final-barline';
26
32
  ```
27
33
 
28
34
  ```sh
@@ -37,5 +43,68 @@ import Barline from '@coreusa/final-barline';
37
43
  lineWidth={2}
38
44
  yMaxValuePadding={1}
39
45
  />
46
+ ```
47
+
48
+ ## Data series
49
+
50
+ Data is expected as an array of data series objects. Please note that each data series must have the same length, as different lengths is not currently supported.
51
+
52
+ ```sh
53
+ # Data structure
54
+ data = [
55
+ {
56
+ label: 'Some interesting data',
57
+ values: [0, 1, 2, 3, 4, 5]
58
+ },
59
+ {
60
+ label: 'Even more interesting data',
61
+ values: [15, 22, 14, 30, 20, 18]
62
+ },
63
+ ...and so on...
64
+ ]
65
+ ```
66
+
67
+ ## Custom X values
68
+ By default x values are set to the index of the dataseries, meaning the X-axis will go from 0 -> N where N is the data series length. You can provide custom X values if the standard x values aren't desired:
69
+
70
+ ```sh
71
+ # Setup custom X Values
72
+ let customXValues = ['A', 'B', 'C', 'D', 'E', 'F']
40
73
 
74
+ <Barline
75
+ data={data}
76
+ xValues={customXValues}
77
+ type="line"
78
+ height={400}
79
+ width={600}
80
+ title="Chart title"
81
+ lineWidth={2}
82
+ yMaxValuePadding={1}
83
+ />
41
84
  ```
85
+
86
+ ## Options
87
+ Final Barline has a wide array of customization options to alter the chart to your liking.
88
+
89
+ - `responsive` - Turn responsiveness of the chart on or off. Default: `TRUE`
90
+ - `title` - Title of the chart. Placed top center if provided. Default: NONE
91
+ - `width` - Provide a custom width for the chart. Note that this requires `responsive` to be `FALSE`
92
+ - `height` - Provide a custom height for the chart. Default: `220`
93
+ - `xValueSuffix` - Text placed after each X tick label (also shown in tooltip when hovering the graph)
94
+ - `yValueSuffix` - Text placed after each Y tick label (also shown in tooltip header when hovering the graph)
95
+ - `lineWidth` - Width of each data serie's graph line. Only applicable when `type = line` (line graph)
96
+ - `paddingSides` - Provide your own padding to each side of the chart. Options are:
97
+ - `paddingSides.left` - Padding to the left of the chart
98
+ - `paddingSides.right` - Padding to the right of the chart
99
+ - `paddingSides.top` - Padding to the top of the chart
100
+ - `paddingSides.bottom` - Padding to the bottom of the chart
101
+ - `xValueCulling` - By default all x values are shown. Sometimes when there are alot of data points, the graph will get unreadable. Use xValueCulling to limit how many x values are shown
102
+ - `yValueCulling` - Same as `xValueCulling`, but for the y values
103
+ - `xMin, xMax, yMin, yMax` - By default Final Barline calculates the min/max for x and y. If you'd instead like to set these values yourself, use these
104
+ - `showLegend` - Turn on/off the data series legend. Default: `TRUE`
105
+ - `xValuePrecision, yValuePrecision` - Number precision formatting to apply to the x and y values
106
+ - `showHorizontalGridLines` - Whether or not to display horizontal (Y axis and to the right) gridlines. Default: `TRUE`
107
+ - `showVerticalGridLines` - Whether or not to display vertical gridlines (X axis and down). Default: `TRUE`
108
+ - `yMaxValuePadding` - Apply padding to the max value of Y. Useful if some additional space is needed at the top. Not to be confused with `paddingSides.top` which alters where the chart starts. Default: `0`
109
+
110
+ _Header image credit: Square Enix_
@@ -81,6 +81,7 @@
81
81
  } = $props();
82
82
 
83
83
  let chartContainerWidth = $state(0);
84
+ let tooltipWidth = $state(0);
84
85
  let showGraphGrid = $state(true);
85
86
  let svgChartElement: SVGSVGElement;
86
87
  // Start position of the X/Y axis, to the right of the Y axis
@@ -117,9 +118,8 @@
117
118
  const xScale = (x: number) => {
118
119
  // TODO: Add padding to computedXMax if need for padding on the right side
119
120
  // NOTE: Added padding to the right side to ensure scale factor has enough room
120
- const ratio = (x - computedXMin) / (computedXMax + 1 - computedXMin);
121
- const res =
122
- chartSafetyMarginX / 2 + paddingSides.left + paddingSides.right + ratio * chartWidth;
121
+ const ratio = (x - computedXMin) / (computedXMax - computedXMin);
122
+ const res = ratio * chartWidth;
123
123
  // Clamp to the drawable area
124
124
  return Math.max(paddingSides.left + paddingSides.right, Math.min(chartWidth, res));
125
125
  };
@@ -162,7 +162,15 @@
162
162
  */
163
163
  let mouseHoverX = $derived(dataHoverIndex >= 0 ? xScale(dataHoverIndex) : 0);
164
164
 
165
- let tooltipX = $derived(mouseHoverX + 10);
165
+ let tooltipX = $derived.by(() => {
166
+ // Determine when the mouse is hovering so far to the right that there's not enough space to show the tooltip and its width
167
+ if (mouseHoverX > chartWidth - tooltipWidth - 10) {
168
+ return mouseHoverX - tooltipWidth;
169
+ } else {
170
+ // Mouse is hovering to the left half of the chart, tooltip is shown to the right of the mouse cursor
171
+ return mouseHoverX + 5;
172
+ }
173
+ });
166
174
  let tooltipY = $state(0);
167
175
 
168
176
  let isHoveringChart = $state(false);
@@ -207,8 +215,8 @@
207
215
  // Convert from the dimensions of the image to a corresponding data index in the chart
208
216
  const relative = mouseX / chartWidth;
209
217
  const index = Math.round(computedXMin + relative * (computedXMax - computedXMin));
210
- tooltipY = event.clientY - rect.top + 10; // 10px below the mouse
211
- dataHoverIndex = Math.max(0, Math.min(pointCount - 1, index));
218
+ tooltipY = event.clientY - rect.top; // 10px below the mouse
219
+ dataHoverIndex = Math.max(0, Math.min(pointCount, index));
212
220
  isHoveringChart = true;
213
221
  };
214
222
 
@@ -225,14 +233,14 @@
225
233
  return [];
226
234
  }
227
235
 
228
- const barWidth = innerWidth / pointCount;
236
+ const barWidth = innerWidth / pointCount / 2;
229
237
 
230
238
  const groups = Array.from({ length: pointCount }, (_, dataSeriesIndex) => {
231
239
  const xBaseLine = xScale(dataSeriesIndex);
232
240
  return data.map((series, seriesIndex) => {
233
241
  const v = series.values[dataSeriesIndex];
234
242
  const x = xBaseLine + seriesIndex * barWidth - barWidth / 2;
235
- const y = Math.abs(yScale(v));
243
+ const y = yScale(v);
236
244
  const h = innerHeight + (paddingSides.top + paddingSides.bottom) - y;
237
245
  return {
238
246
  x,
@@ -249,10 +257,13 @@
249
257
  * Creates values for X, taking into account any x culling settings
250
258
  */
251
259
  const xTicks = $derived.by(() => {
260
+ // Ensure the culling value isn't larger than max number of data points
261
+ const xCulling = Math.min(xValueCulling, pointCount - 1);
262
+
252
263
  const range = computedXMax - computedXMin;
253
- const step = Math.max(1, Math.floor(range / xValueCulling));
264
+ const step = Math.max(1, Math.floor(range / xCulling));
254
265
 
255
- const indexes = Array.from({ length: xValueCulling + 1 }, (_, i) => computedXMin + i * step);
266
+ const indexes = Array.from({ length: xCulling + 1 }, (_, i) => computedXMin + i * step);
256
267
 
257
268
  // Use a set to ensure unique values
258
269
  const unique = Array.from(new Set(indexes));
@@ -260,12 +271,20 @@
260
271
  return unique.map((index) => {
261
272
  // TODO: Using base index as fallback will cause issues when adding a xValuePadding and using custom xValues
262
273
  const value = xValues?.[index] !== undefined ? xValues[index] : index;
274
+ let position = xScale(index);
275
+ let labelPosition = position;
276
+ if (index === 0) {
277
+ labelPosition = position + 10;
278
+ } else if (index === pointCount - 1) {
279
+ labelPosition = position - 10;
280
+ }
263
281
  return {
264
282
  value:
265
283
  timeFormatting && value === 0
266
284
  ? 'Now'
267
285
  : `${value.toFixed(xValuePrecision)}${xValueSuffix}`,
268
- position: xScale(index)
286
+ position: position,
287
+ labelPosition: labelPosition
269
288
  };
270
289
  });
271
290
  });
@@ -391,7 +410,7 @@
391
410
  {#if isEnoughDataForPresentation}
392
411
  <!-- Axis labels and grid lines -->
393
412
  <!-- X-axis -->
394
- {#each xTicks as { value, position }, index (`x-axis-label-${index}`)}
413
+ {#each xTicks as { value, position, labelPosition }, index (`x-axis-label-${index}`)}
395
414
  {#if showGraphGrid}
396
415
  <!-- LINES - Vertical -->
397
416
  <line
@@ -405,7 +424,7 @@
405
424
  {/if}
406
425
  <!-- LABELS - X axis -->
407
426
  <text
408
- x={position}
427
+ x={labelPosition}
409
428
  y={innerHeight + verticalPadding + 15}
410
429
  text-anchor="middle"
411
430
  font-size="12"
@@ -421,8 +440,8 @@
421
440
  {#if showGraphGrid}
422
441
  <!-- LINES - Horizontal -->
423
442
  <line
424
- x1={showHorizontalGridLines ? chartSafetyMarginX / 2 + 20 : verticalPadding}
425
- x2={showHorizontalGridLines ? chartWidth + chartSafetyMarginX : horizontalPadding - 5}
443
+ x1={showHorizontalGridLines ? chartSafetyMarginX / 2 : verticalPadding}
444
+ x2={showHorizontalGridLines ? chartWidth : horizontalPadding - 5}
426
445
  y1={y}
427
446
  y2={y}
428
447
  stroke="#ccc"
@@ -504,8 +523,8 @@
504
523
  <line
505
524
  x1={0}
506
525
  x2={chartContainerWidth}
507
- y1={tooltipY - 10}
508
- y2={tooltipY - 10}
526
+ y1={tooltipY + 1}
527
+ y2={tooltipY + 1}
509
528
  stroke="#2B9C6A"
510
529
  stroke-width="1"
511
530
  stroke-dasharray="4 4"
@@ -527,6 +546,7 @@
527
546
  <!-- Tooltip for the graph -->
528
547
  {#if isHoveringChart && dataHoverIndex >= 0}
529
548
  <div
549
+ bind:clientWidth={tooltipWidth}
530
550
  class="barline-tooltip"
531
551
  style="
532
552
  left: {tooltipX}px;
@@ -557,14 +577,14 @@
557
577
  </div>
558
578
  </div>
559
579
  {/if}
560
- <div class="d-flex justify-content-center">
580
+ <div class="export-button-container">
561
581
  <button
562
582
  class="barline-export-chart-button"
563
583
  onclick={() => {
564
584
  exportSvg(svgChartElement);
565
585
  }}
566
586
  >
567
- Export graph
587
+ 🖼️ Export
568
588
  </button>
569
589
  </div>
570
590
  </div>
@@ -572,22 +592,7 @@
572
592
  <style>
573
593
  .barline-chart {
574
594
  font-family: 'Open Sans', 'Lato', 'Helvetica', 'Ubuntu', sans-serif;
575
- }
576
-
577
- .barline-export-chart-button {
578
- background-color: #222;
579
- color: #fff;
580
- border: none;
581
- padding: 8px 16px;
582
- border-radius: 4px;
583
- font-size: 14px;
584
- cursor: pointer;
585
- }
586
-
587
- .barline-export-chart-button:hover {
588
- background-color: #3f3f3f;
589
- color: #ffac11;
590
- transition: all 0.25s ease-in-out;
595
+ position: relative;
591
596
  }
592
597
 
593
598
  .barline-tooltip {
@@ -600,10 +605,6 @@
600
605
  z-index: 500;
601
606
  }
602
607
 
603
- .barline-chart-title {
604
- font-weight: bold;
605
- }
606
-
607
608
  .barline-tooltip-header {
608
609
  /*bg-dark-blue-horizontal-gradient text-white p-2*/
609
610
  background-color: #222;
@@ -621,4 +622,24 @@
621
622
  .barline-tooltip-content dd {
622
623
  margin: 0;
623
624
  }
625
+
626
+ .barline-export-chart-button {
627
+ background-color: #222;
628
+ color: #fff;
629
+ border: none;
630
+ padding: 8px 16px;
631
+ border-radius: 4px;
632
+ font-size: 14px;
633
+ cursor: pointer;
634
+ }
635
+
636
+ .barline-export-chart-button:hover {
637
+ background-color: #3f3f3f;
638
+ color: #ffac11;
639
+ transition: all 0.25s ease-in-out;
640
+ }
641
+
642
+ .barline-chart-title {
643
+ font-weight: bold;
644
+ }
624
645
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreusa/final-barline",
3
- "version": "0.1.0",
3
+ "version": "0.1.5",
4
4
  "description": "Final Barline - The lightweight, high performance SVG Chart Library",
5
5
  "keywords": [
6
6
  "SVG Chart",