svelte-plots-basic 3.4.0 → 4.1.0

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/NEWS.md ADDED
@@ -0,0 +1,76 @@
1
+ # Release notes
2
+
3
+ This file contains the full release history of `svelte-plots-basic`. The [README](README.md) shows only the most recent releases.
4
+
5
+ ## 4.1.0
6
+
7
+ * Added a `svelte` export condition to every entry point, so bundlers recognize the package as a Svelte library. This matters when `prebundleSvelteLibraries` is disabled, where the components must be excluded from dependency pre-bundling.
8
+ * Added `./package.json` to the exported paths, so tools that read the package manifest can resolve it.
9
+
10
+ ## 4.0.0
11
+
12
+ * Added horizontal legend layout and improved legend sizing and alignment.
13
+ * Improved PNG/SVG export reliability, export-dialog accessibility, and clipboard handling.
14
+ * Strengthened validation, automatic axis ticks, marker rendering, and component cleanup.
15
+ * Made subscripts, superscripts, and tick-factor labels consistent across modern browsers.
16
+ * Updated to `mdatools ^1.5.0`, now declared as a peer dependency to prevent incompatible duplicate instances.
17
+
18
+ ## 3.4.0
19
+
20
+ * Added the `plotActions` property to `Axes` for adding custom buttons to the download panel.
21
+ * Added advanced validation for axis limits, text-label positions, heatmap breaks, and custom plot actions.
22
+ * Fixed text-label positioning and rotation when individual positions are provided.
23
+ * Fixed heatmaps with automatic, custom, or constant-value breaks.
24
+ * Added support for rectangular and constant-height 3D meshes.
25
+ * Improved PNG, SVG, and clipboard export by serializing a clone instead of modifying the visible plot.
26
+ * Fixed the advanced PNG export dialog so a default resolution is always selected, falling back to 300 dpi when necessary.
27
+ * Prevented plot controls from submitting an enclosing HTML form.
28
+ * Added package validation before publishing and synchronized Svelte 5 peer-dependency metadata.
29
+ * Fixed conditionally rendered axes, boxes, and legends so their parent state is cleared when they are removed.
30
+ * Improved heatmap interval processing and added automatically generated colormaps with more than 16 colors.
31
+ * Fixed `TextLegend` positioning at zero and strengthened finite-range validation for 3D axes.
32
+ * Reduced reactive and repeated allocation overhead for axis configuration, text measurement, PNG export, and colormap generation.
33
+ * Added dependency-free regression tests and made them run automatically before packaging.
34
+ * Documented the trust requirements for labels rendered as SVG/HTML markup.
35
+
36
+ ## 3.3.0
37
+
38
+ * Added advanced "save as PNG" option where user can select size and resolution of the image as well as change the filename.
39
+ * several small improvements and bug fixes.
40
+
41
+
42
+ ## 3.2.1
43
+
44
+ * Added mouse click handler option for `Heatmap.svelte` component (returns row and column index of the element).
45
+
46
+
47
+ ## 3.1.1-3.1.6
48
+
49
+ * Fixed bug which sometimes caused an error when manual text tick labels provided.
50
+ * Added `markerSize` specification to legend items.
51
+ * Small improvements and bug fixes.
52
+
53
+ ## 3.1.0
54
+
55
+ * Added possibility to reverse the axis, just swap the limit values.
56
+
57
+
58
+ ## 3.0.0
59
+
60
+ New major release (v. 3.0.0, released 20/01/2025) introduces many breaking changes as the library has been re-written using Svelte 5. If you use previous versions of `svelte-plots-basic` in your projects, and do not want to change anything, stick to the latest 2.x.x version.
61
+
62
+ In addition to Svelte 5 syntax and functionality, this release also introduces a lot of improvements, such as better handling of axis ticks, new syntax for axis elements, etc. See examples below for inspiration. Here is a short list what has been changed from 2.x.x:
63
+
64
+ * **No more slots** — components `<Box>`, `<XAxis>`, `<YAxis>`, and `<ZAxis>` (for 3D) do not require attribute `slot` anymore. Moreover, you have to remove this attribute from all your old code in order to use the new version.
65
+
66
+ * **Axis labels** — in version 2.x.x. labels for x- and y-axis were a part of `<Axes>` component. From 3.x.x. they are part of corresponding axis components, for example:<br> `<XAxis label="x-axis label"/>`.
67
+
68
+ * **Support for subscripts and superscripts** — you can now use simple syntax for subscripts (`_`) and superscripts (`^`) in axis labels, tick labels, legend labels and plot title. For example, such labels as `'x^2'`, `x^-1` or `x_(34)` — will be correctly transformed to corresponding SVG text elements in order to visualize them correctly. You can also use HTML symbols, such as `&alpha;`.
69
+
70
+ * **Mouse events** — previosly, handling mouse events was done through a coplex system of manual events dispatched by `<Axes>` component. From 3.x.x this is much easier, almost every 2D series component (`<Rectangles>`, `<Bars>`, `<Points>`, `<Segments>`, `<Lines>`, `<Areas>`) can handle its own `onclick` event. You just need to provide a callback — a function that will be run if this event is fired. The callback should have one argument — id (position) of an element the click was made on. For example `<Points ... onclick={(id) => console.log(id)}>`. Because of this modification, the property `title` has been removed from all components. 2D component `<Axes>` also supports `onclick` event, it provides coordinates of the clicked point as arguments for the callback function. 3D plots do not support mouse events.
71
+
72
+ * **Save and copy 3D plots** — from 3.x.x you can also save and copy to clipboard 3D plots (same way as for 2D).
73
+
74
+ * **Doc strings** — every component has a corresponding doc string with description of its properties and a simple code example. It should be available when you move your mouse over the component tag in your editor/IDE if it supports this option (works in VSCode).
75
+
76
+ * **Properites** — from 3.x.x the naming of the properties is more consistent. For example, before the library used properties `borderColor` and `borderWidth` for areas, rectangels, bars and markers, while for lines, segments and multilines the similar properties were named as `lineColor` and `lineWidth`. Now they all have prefix `line*` if it is something about lines or segments and `face*` if it is somthing inside a closed contour. So no more `border*` properties.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `svelte-plots-basic` is a [Svelte](https://svelte.dev) component library for making simple responsive 2D and 3D plots/charts. The plots are created by generating [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) inside HTML document.
4
4
 
5
- One can think of this library as "Lego" bricks for plots. It has two groups of components: for 2D and for 3D plots. The 2D components are more developed while 3D components remain quite basic. In addition to `svelte`, the package has one direct dependence, [mdatools-js](https://github.com/svkucheryavski/mdatools-js) library, which is used for vector/matrix operations, statistics, and other manipulations with data values.
5
+ One can think of this library as "Lego" bricks for plots. It has two groups of components: for 2D and for 3D plots. The 2D components are more developed while 3D components remain quite basic. In addition to `svelte`, the package requires the [mdatools-js](https://github.com/svkucheryavski/mdatools-js) library as a peer dependency. It is used for vector/matrix operations, statistics, and other manipulations with data values.
6
6
 
7
7
 
8
8
  ## Showcase
@@ -15,6 +15,19 @@ These websites and web-applications use `svelte-plots-basic` library:
15
15
 
16
16
  ## News
17
17
 
18
+ ### 4.1.0
19
+
20
+ * Added a `svelte` export condition to every entry point, so bundlers recognize the package as a Svelte library. This matters when `prebundleSvelteLibraries` is disabled, where the components must be excluded from dependency pre-bundling.
21
+ * Added `./package.json` to the exported paths, so tools that read the package manifest can resolve it.
22
+
23
+ ### 4.0.0
24
+
25
+ * Added horizontal legend layout and improved legend sizing and alignment.
26
+ * Improved PNG/SVG export reliability, export-dialog accessibility, and clipboard handling.
27
+ * Strengthened validation, automatic axis ticks, marker rendering, and component cleanup.
28
+ * Made subscripts, superscripts, and tick-factor labels consistent across modern browsers.
29
+ * Updated to `mdatools ^1.5.0`, now declared as a peer dependency to prevent incompatible duplicate instances.
30
+
18
31
  ### 3.4.0
19
32
 
20
33
  * Added the `plotActions` property to `Axes` for adding custom buttons to the download panel.
@@ -39,41 +52,7 @@ These websites and web-applications use `svelte-plots-basic` library:
39
52
  * several small improvements and bug fixes.
40
53
 
41
54
 
42
- ### 3.2.1
43
-
44
- * Added mouse click handler option for `Heatmap.svelte` component (returns row and column index of the element).
45
-
46
-
47
- ### 3.1.1-3.1.6
48
-
49
- * Fixed bug which sometimes caused an error when manual text tick labels provided.
50
- * Added `markerSize` specification to legend items.
51
- * Small improvements and bug fixes.
52
-
53
- ### 3.1.0
54
-
55
- * Added possibility to reverse the axis, just swap the limit values.
56
-
57
-
58
- ### 3.0.0
59
-
60
- New major release (v. 3.0.0, released 20/01/2025) introduces many breaking changes as the library has been re-written using Svelte 5. If you use previous versions of `svelte-plots-basic` in your projects, and do not want to change anything, stick to the latest 2.x.x version.
61
-
62
- In addition to Svelte 5 syntax and functionality, this release also introduces a lot of improvements, such as better handling of axis ticks, new syntax for axis elements, etc. See examples below for inspiration. Here is a short list what has been changed from 2.x.x:
63
-
64
- * **No more slots** — components `<Box>`, `<XAxis>`, `<YAxis>`, and `<ZAxis>` (for 3D) do not require attribute `slot` anymore. Moreover, you have to remove this attribute from all your old code in order to use the new version.
65
-
66
- * **Axis labels** — in version 2.x.x. labels for x- and y-axis were a part of `<Axes>` component. From 3.x.x. they are part of corresponding axis components, for example:<br> `<XAxis label="x-axis label"/>`.
67
-
68
- * **Support for subscripts and superscripts** — you can now use simple syntax for subscripts (`_`) and superscripts (`^`) in axis labels, tick labels, legend labels and plot title. For example, such labels as `'x^2'`, `x^-1` or `x_(34)` — will be correctly transformed to corresponding SVG text elements in order to visualize them correctly. You can also use HTML symbols, such as `&alpha;`.
69
-
70
- * **Mouse events** — previosly, handling mouse events was done through a coplex system of manual events dispatched by `<Axes>` component. From 3.x.x this is much easier, almost every 2D series component (`<Rectangles>`, `<Bars>`, `<Points>`, `<Segments>`, `<Lines>`, `<Areas>`) can handle its own `onclick` event. You just need to provide a callback — a function that will be run if this event is fired. The callback should have one argument — id (position) of an element the click was made on. For example `<Points ... onclick={(id) => console.log(id)}>`. Because of this modification, the property `title` has been removed from all components. 2D component `<Axes>` also supports `onclick` event, it provides coordinates of the clicked point as arguments for the callback function. 3D plots do not support mouse events.
71
-
72
- * **Save and copy 3D plots** — from 3.x.x you can also save and copy to clipboard 3D plots (same way as for 2D).
73
-
74
- * **Doc strings** — every component has a corresponding doc string with description of its properties and a simple code example. It should be available when you move your mouse over the component tag in your editor/IDE if it supports this option (works in VSCode).
75
-
76
- * **Properites** — from 3.x.x the naming of the properties is more consistent. For example, before the library used properties `borderColor` and `borderWidth` for areas, rectangels, bars and markers, while for lines, segments and multilines the similar properties were named as `lineColor` and `lineWidth`. Now they all have prefix `line*` if it is something about lines or segments and `face*` if it is somthing inside a closed contour. So no more `border*` properties.
55
+ Notes for earlier releases, including the list of breaking changes introduced in 3.0.0 when the library was re-written using Svelte 5, are available in the [full release notes](https://github.com/svkucheryavski/svelte-plots-basic/blob/main/NEWS.md).
77
56
 
78
57
 
79
58
  ## Security and label markup
@@ -96,6 +75,8 @@ or, to install it with yarn:
96
75
  yarn add svelte-plots-basic
97
76
  ```
98
77
 
78
+ The components are always imported from the `svelte-plots-basic/2d` and `svelte-plots-basic/3d` subpaths, and the helper functions from `svelte-plots-basic/utils` and `svelte-plots-basic/constants`. There is no root import — the 2D and 3D groups deliberately share component names, such as `Axes` and `Points`, so importing from `svelte-plots-basic` directly is not supported and will fail to resolve.
79
+
99
80
 
100
81
  ## User tutorial (2D plots)
101
82
 
@@ -220,7 +201,7 @@ You can also define tick positions and corresponding labels manually. Tick posit
220
201
 
221
202
  ```svelte
222
203
  <Axes limX={[2019, 2026]} limY={[0, 60000]}>
223
- <Xaxis ticks={[2023, 2024, 2025]} tickLabels={['Past', 'Present', 'Future']} />
204
+ <XAxis ticks={[2023, 2024, 2025]} tickLabels={['Past', 'Present', 'Future']} />
224
205
  </Axes>
225
206
  ```
226
207
 
@@ -620,7 +601,7 @@ Here is an example:
620
601
 
621
602
  ```svelte
622
603
  <script>
623
- import { Axes, Xaxis, YAxis, Box, Area } from 'svelte-plots-basic/2d';
604
+ import { Axes, XAxis, YAxis, Box, Area } from 'svelte-plots-basic/2d';
624
605
 
625
606
  const xValues = [3, 2, 2.5, 4, 5.5, 6, 5];
626
607
  const yValues = [1, 3, 4, 5, 4, 3, 1];
@@ -677,7 +658,7 @@ Here is an example:
677
658
 
678
659
  ```svelte
679
660
  <script>
680
- import { Axes, Xaxis, YAxis, Box, Heatmap } from 'svelte-plots-basic/2d';
661
+ import { Axes, XAxis, YAxis, Box, Heatmap } from 'svelte-plots-basic/2d';
681
662
  import { Matrix } from 'mdatools/arrays';
682
663
 
683
664
  // create 5 x 10 matrix filled with normally distributed random values
@@ -729,7 +710,7 @@ Colormap legend can be used together with `<Heatmap>` or any other color groupin
729
710
 
730
711
  ```svelte
731
712
  <script>
732
- import { Axes, Xaxis, YAxis, Box, Heatmap, ColormapLegend } from 'svelte-plots-basic/2d';
713
+ import { Axes, XAxis, YAxis, Box, Heatmap, ColormapLegend } from 'svelte-plots-basic/2d';
733
714
  import { Matrix } from 'mdatools/arrays';
734
715
 
735
716
  // create 5 x 10 matrix filled with normally distributed random values
@@ -789,16 +770,16 @@ Here is an example:
789
770
  const y3 = x.apply(v => Math.pow(v, 4));
790
771
 
791
772
  // define properties of each series
792
- line1Props = {lineType: 3, lineColor: 'red'};
793
- line2Props = {lineColor: 'blue', lineType: 1};
794
- point2Props = {lineColor: 'blue', faceColor: 'white'};
795
- point3Props = {marker: 5, lineColor: 'green', faceColor: 'yellow'};
773
+ const line1Props = {lineType: 3, lineColor: 'red'};
774
+ const line2Props = {lineColor: 'blue', lineType: 1};
775
+ const point2Props = {lineColor: 'blue', faceColor: 'white'};
776
+ const point3Props = {marker: 5, lineColor: 'green', faceColor: 'yellow'};
796
777
 
797
778
  // legend items
798
- items = [
799
- {label: 'y=x^2', line: lines1Props },
800
- {label: "y=x^3", line: lines2Props, point: points2Props },
801
- {label: "y=x^4", point: points2Props },
779
+ const items = [
780
+ {label: 'y=x^2', line: line1Props },
781
+ {label: "y=x^3", line: line2Props, point: point2Props },
782
+ {label: "y=x^4", point: point3Props },
802
783
  ];
803
784
  </script>
804
785
 
@@ -812,7 +793,7 @@ Here is an example:
812
793
  <Points xValues={x} yValues={y2} {...point2Props} />
813
794
 
814
795
  <!-- series 3: markers in form of diamonds with green stroke and yellow fill -->
815
- <Points xValues={x} yValues={y3} {...points3Props} />
796
+ <Points xValues={x} yValues={y3} {...point3Props} />
816
797
 
817
798
  <!-- legend with one JSON for each series -->
818
799
  <Legend position="right" {items} />
@@ -826,12 +807,21 @@ Check this example in Svelte REPL: [plots-legend](https://svelte.dev/playground/
826
807
 
827
808
  The `position` parameter can be one of the follows: `'topleft'`, `'top'`, `'topright'`, `'right'`, `'bottomright'` and so on.
828
809
 
810
+ By default, legend items are arranged vertically. Set `orientation="horizontal"` to arrange them in a single row, which is especially useful with the `top` and `bottom` positions:
811
+
812
+ ```svelte
813
+ <Legend position="top" orientation="horizontal" {items} />
814
+ ```
815
+
816
+ The horizontal layout does not wrap items onto additional rows, so a large number of items or long labels may require more plot width.
817
+
829
818
  The component has also properties which changes it apearance, here is the full list:
830
819
 
831
820
  Property name | Default value | Description
832
821
  --|--|--
833
822
  `items` | | array with JSON properties of legend items
834
823
  `position` | `'topleft'` | position of the legend element inside plotting area
824
+ `orientation` | `'vertical'` | arrangement of legend items: `'vertical'` or `'horizontal'`
835
825
  `lineColor` | `'#303030'` | color of the legend box line
836
826
  `lineWidth` | `1` | width (thickness) of the legend box line
837
827
  `faceColor` | `'#fff'` | background color of the legend box
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "svelte-plots-basic",
3
3
  "description": "Svelte components for creating simple plots",
4
- "version": "3.4.0",
4
+ "version": "4.1.0",
5
5
  "author": "Sergey Kucheryavskiy",
6
6
  "license": "MIT",
7
7
  "private": false,
@@ -12,22 +12,22 @@
12
12
  "prepack": "npm run check && npm test"
13
13
  },
14
14
  "exports": {
15
- "./constants": "./src/constants.js",
16
- "./utils": "./src/methods.js",
17
- "./2d": "./src/2d/index.js",
18
- "./2d/*.svelte": "./src/2d/*.svelte",
19
- "./3d": "./src/3d/index.js",
20
- "./3d/*.svelte": "./src/3d/*.svelte"
15
+ "./constants": { "svelte": "./src/constants.js", "default": "./src/constants.js" },
16
+ "./utils": { "svelte": "./src/methods.js", "default": "./src/methods.js" },
17
+ "./2d": { "svelte": "./src/2d/index.js", "default": "./src/2d/index.js" },
18
+ "./2d/*.svelte": { "svelte": "./src/2d/*.svelte", "default": "./src/2d/*.svelte" },
19
+ "./3d": { "svelte": "./src/3d/index.js", "default": "./src/3d/index.js" },
20
+ "./3d/*.svelte": { "svelte": "./src/3d/*.svelte", "default": "./src/3d/*.svelte" },
21
+ "./package.json": "./package.json"
21
22
  },
22
23
  "devDependencies": {
23
- "svelte": "^5.x.x"
24
+ "mdatools": "^1.5.0",
25
+ "svelte": "^5.56.8"
24
26
  },
25
27
  "peerDependencies": {
28
+ "mdatools": "^1.5.0",
26
29
  "svelte": "^5.0.0"
27
30
  },
28
- "dependencies": {
29
- "mdatools": "^1.x.x"
30
- },
31
31
  "keywords": [
32
32
  "svelte",
33
33
  "plots",
@@ -35,7 +35,8 @@
35
35
  "svelte plots"
36
36
  ],
37
37
  "files": [
38
- "src"
38
+ "src",
39
+ "NEWS.md"
39
40
  ],
40
41
  "repository": {
41
42
  "type": "git",
@@ -2,12 +2,12 @@
2
2
  @component Adds a closed polygon (area).
3
3
 
4
4
  Main properties:
5
- - `xValues` - array of vector with x-coordinates of the polygon's points.
6
- - `yValues` - array of vector with y-coordinates of the polygon's points.
7
- - `lineWidth` - width (thickness) of the polygon's line in pixels, defailt: `1`.
5
+ - `xValues` - array or vector with x-coordinates of the polygon's points.
6
+ - `yValues` - array or vector with y-coordinates of the polygon's points.
7
+ - `lineWidth` - width (thickness) of the polygon's line in pixels, default: `1`.
8
8
  - `lineColor` - line color, default: `Colors.PRIMARY`.
9
9
  - `lineType` - type of line (`1` - solid, `2` - dashed, `3` - dotted, `4` - dashdot).
10
- - `facColor` - face (fill) color of the polygon, default: `'transparent'`.
10
+ - `faceColor` - face (fill) color of the polygon, default: `'transparent'`.
11
11
  - `opacity` - opacity of the face color, default: `1`.
12
12
  - `onclick` - function (callback) to be called when user clicks on a polygon.
13
13
 
@@ -29,7 +29,7 @@
29
29
  <script>
30
30
  import { getContext } from 'svelte';
31
31
  import { Colors } from '../constants';
32
- import { val2p, checkCoords, handleClick, transformCoords } from '../methods';
32
+ import { val2p, checkCoords, handleClick, transformCoords, normalizeLineType } from '../methods';
33
33
  import { LINE_STYLES } from '../constants';
34
34
 
35
35
  let {
@@ -38,7 +38,7 @@
38
38
  lineColor = Colors.PRIMARY, // color of segment lines
39
39
  lineType = 1, // type of segment lines (1 - solid, 2 - dashed, 3 - dotted, 4 - dashdot)
40
40
  lineWidth = 1, // width (thickness) of segment lines
41
- faceColor = 'transparent', // color of segment lines
41
+ faceColor = 'transparent', // face (fill) color of the polygon
42
42
  opacity = 1, // opacity of the area color
43
43
  onclick, // function to be called if onclick event fires
44
44
  } = $props();
@@ -46,6 +46,7 @@
46
46
  // check user provided coordinates
47
47
  const xv = $derived(checkCoords(xValues, 'Area'));
48
48
  const yv = $derived(xv ? checkCoords(yValues, 'Area', xv.length) : null);
49
+ const validLineType = $derived(normalizeLineType(lineType, 'Area'));
49
50
 
50
51
  // get axes context and compute coordinates of polyline
51
52
  const axes = getContext('axes');
@@ -55,7 +56,7 @@
55
56
  const xs = $derived(xv ? transformCoords(xv.subset(1), axes.tX()) : null);
56
57
  const pa = $derived(p && xs && ys ? xs + ',' + ys + ' ' + p + ' ' + xs + ',' + ys : null);
57
58
 
58
- const areaStyleStr = $derived(`opacity:${opacity};fill:${faceColor};stroke:${lineColor};stroke-width: ${lineWidth}px;stroke-dasharray:${LINE_STYLES[axes.scales().plot][lineType - 1]}`);
59
+ const areaStyleStr = $derived(`opacity:${opacity};fill:${faceColor};stroke:${lineColor};stroke-width: ${lineWidth}px;stroke-dasharray:${LINE_STYLES[axes.scales().plot][validLineType - 1]}`);
59
60
  </script>
60
61
 
61
62
  {#if pa}
@@ -5,7 +5,7 @@
5
5
  - `limX` - array with limits for x-axis (in plot units), default: `[0, 1]`.
6
6
  - `limY` - array with limits for y-axis (in plot units), default: `[0, 1]`.
7
7
  - `title` - title of the plot (string).
8
- - `margins` - array with relative margins (bottom, left, top, right), default `[1.0, 0.85, 0.5, 0.5]`.
8
+ - `margins` - array with relative margins (bottom, left, top, right), default `[1.0, 1.1, 0.55, 0.60]`.
9
9
 
10
10
  Properties for downloading the plot as PNG or SVG file or copying it to clipboard:
11
11
  - `downloadLinks` - how to show download links panel (`'none'` - default, `'hover'`, `'fixed'`).
@@ -13,8 +13,8 @@
13
13
  - `pngWidth` - width of PNG image in cm, default `8`
14
14
  - `pngHeight` - height of PNG image in cm, default `8`.
15
15
  - `pngRes` - resolution of PNG image (pixels per inch), default `300`.
16
- - `clipboardWidth` - width of plot image to copy to clipboard in pixels.
17
- - `clipboardHeight` - height of plot image to copy to clipboard in pixels.Axes
16
+ - `clipboardWidth` - width of plot image to copy to clipboard in pixels, default `1200`.
17
+ - `clipboardHeight` - height of plot image to copy to clipboard in pixels, default `800`.
18
18
 
19
19
  Example:
20
20
 
@@ -23,7 +23,7 @@
23
23
  import {Axes} from 'svelte-plots-basic/2d';
24
24
  </script>
25
25
 
26
- <Axes limX={[2000, 2050]} limY={[0, 100]} title="GDP of contries" >
26
+ <Axes limX={[2000, 2050]} limY={[0, 100]} title="GDP of countries" >
27
27
  </Axes>
28
28
  ```
29
29
  -->
@@ -101,7 +101,7 @@
101
101
 
102
102
  /* handler for mouse events */
103
103
  function handleMouse(e, f) {
104
- if (f && e.target.id == "axes-box") {
104
+ if (f && e.target.id === "axes-box") {
105
105
  const x = invTransformCoords([e.offsetX - left], tX)[0];
106
106
  const y = invTransformCoords([e.offsetY - top], tY)[0];
107
107
  f(x, y);
@@ -307,7 +307,7 @@
307
307
  <text {x} y={coords.ltop} dx="1.2em" dy="-0.25em" dominant-baseline="hanging"
308
308
  fill={params.labelColor}
309
309
  font-size="{params.fontSize}em"
310
- text-anchor="end"
310
+ text-anchor="start"
311
311
  >{@html getTickFactorLabel(params.labelsFactor)}</text>
312
312
  {/if}
313
313
  {/snippet}
@@ -316,14 +316,16 @@
316
316
  {#snippet glgSnippet(params, coords)}
317
317
  {@const elw = coords.elw + 2 * coords.elp}
318
318
  {@const elp = coords.elp}
319
+ {@const elx = coords.elx}
319
320
  {@const elh = coords.elh}
320
321
  {@const ely = coords.ely}
322
+ {@const eliw = coords.eliw}
321
323
 
322
324
  <svg x={coords.lgl} y={coords.lgt} height={coords.lgh} width={coords.lgw}>
323
325
  <rect height="100%" width="100%" fill={params.faceColor} stroke={params.lineColor} stroke-width={params.lineWidth} />
324
326
 
325
327
  {#each params.items as item, i}
326
- <svg x={0} y={ely[i]} width={coords.lgw} height={elh[i]}>
328
+ <svg x={elx[i]} y={ely[i]} width={eliw[i]} height={elh[i]}>
327
329
 
328
330
  <!-- line -->
329
331
  {#if item.line}
@@ -532,7 +534,7 @@
532
534
  </p>
533
535
  {:else}
534
536
  <div class="download-links">
535
- <button type="button" onclick={handleClickSVG}>↓ svg</button>
537
+ <button type="button" onclick={handleClickSVG}>↓ svg</button>
536
538
  <button type="button" onclick={handleClickPNG}>↓ png</button>
537
539
  <button type="button" onclick={handleClickPNGAdvanced}>↓ png+</button>
538
540
  <button type="button" onclick={handleClickCopy}>⧉ copy</button>
@@ -3,21 +3,19 @@
3
3
 
4
4
  Main properties:
5
5
  - `lineCoords` - coordinates of the line segments as 2D array.
6
- - `lineColor` - color of the box line, default: `Colors.DARKGRAY`.
7
- - `lineWidth` - width of box line in pixels, default: `1`.
6
+ - `lineColor` - color of the line segments, default: `Colors.DARKGRAY`.
7
+ - `lineWidth` - width of the line segments in pixels, default: `1`.
8
8
  - `lineType` - line type (`1` - solid (default), `2` - dashed, `3` - dotted, `4` - dashdot).
9
9
  - `className` - string with classname to be added to `<g></g>` tag wrapping the lines.
10
10
 
11
11
  **Description:**
12
12
 
13
- The coordinates msut be provided in form of nested arrays. Each element of this array contains
13
+ The coordinates must be provided in form of nested arrays. Each element of this array contains
14
14
  `Vector` or `Array` with the coordinates in world (not screen) coordinate system:
15
15
  - `lineCoords[0][0]` - x-coordinates of start points of segments.
16
16
  - `lineCoords[1][0]` - x-coordinates of end points of segments.
17
17
  - `lineCoords[0][1]` - y-coordinates of start points of segments.
18
18
  - `lineCoords[1][1]` - y-coordinates of end points of segments.
19
-
20
- ```
21
19
  -->
22
20
  <script>
23
21
  import { getContext } from 'svelte';
@@ -33,7 +31,7 @@
33
31
  className = '' // CSS class name for the component
34
32
  } = $props();
35
33
 
36
- // get axes context and adjust x margins
34
+ // get axes context for coordinate transforms
37
35
  const axes = getContext('axes');
38
36
 
39
37
  // reactive variables for coordinates of axis lines
@@ -16,8 +16,6 @@
16
16
  - `tickCoords[1][0]` - x-coordinates of end points of tick segments.
17
17
  - `tickCoords[0][1]` - y-coordinates of start points of tick segments.
18
18
  - `tickCoords[1][1]` - y-coordinates of end points of tick segments.
19
-
20
- ```
21
19
  -->
22
20
  <script>
23
21
 
@@ -57,7 +55,7 @@
57
55
  {/each}
58
56
  {:else if las === 2 && pos === 1}
59
57
  {#each x as v, i}
60
- <text data-id={i} x={x[i]} y={y[i]} dx={-dy/2} dy={0} transform={`rotate(-90, ${x[i]}, ${y[i]})`} text-anchor={"end"}>{@html tickLabels[i]}</text>
58
+ <text data-id={i} x={x[i]} y={y[i]} dx={-dy/2} dy={0} transform={`rotate(-90, ${x[i]}, ${y[i]})`} text-anchor="end">{@html tickLabels[i]}</text>
61
59
  {/each}
62
60
  {:else}
63
61
  {#each x as v, i}
@@ -45,25 +45,46 @@
45
45
  onclick
46
46
  } = $props();
47
47
 
48
+ function toNumber(value) {
49
+ const canConvert =
50
+ typeof value === 'number' ||
51
+ (typeof value === 'string' && value.trim() !== '');
52
+
53
+ return canConvert ? Number(value) : NaN;
54
+ }
55
+
48
56
  const bw = $derived.by(() => {
49
- if (barWidth <= 0 || barWidth > 1) {
50
- console.error('Bars: parameter "barWidth" should be between 0 and 1.');
57
+ const value = toNumber(barWidth);
58
+ if (!Number.isFinite(value) || value <= 0 || value > 1) {
59
+ console.error('Bars: parameter "barWidth" must be a finite number greater than 0 and no greater than 1.');
60
+ return null;
61
+ }
62
+ return value;
63
+ });
64
+
65
+ const exactWidth = $derived.by(() => {
66
+ if (barWidthExact == null) return {provided: false, value: null};
67
+
68
+ const value = toNumber(barWidthExact);
69
+ if (!Number.isFinite(value) || value <= 0) {
70
+ console.error('Bars: parameter "barWidthExact" must be a finite positive number.');
51
71
  return null;
52
72
  }
53
- return barWidth;
73
+
74
+ return {provided: true, value};
54
75
  });
55
76
 
56
77
  const x = $derived.by(() => {
57
- if (!bw) return null;
78
+ if (bw === null || exactWidth === null) return null;
58
79
  const xv = checkCoords(xValues, 'BarSeries');
59
80
  if (!xv) return null;
60
81
  const n = xv.length;
61
82
  if (n < 1) return null;
62
83
 
63
84
  let w;
64
- if (barWidthExact) {
65
- w = barWidthExact;
66
- } else if (n == 1) {
85
+ if (exactWidth.provided) {
86
+ w = exactWidth.value;
87
+ } else if (n === 1) {
67
88
  console.error('Bars: if only one bar must be shown, value for property "barWidthExact" should be provided.');
68
89
  return null;
69
90
  } else {
@@ -74,7 +95,7 @@
74
95
  }
75
96
  }
76
97
 
77
- w = w * barWidth;
98
+ w = w * bw;
78
99
  const whalf = w / 2;
79
100
  const left = Vector.zeros(n);
80
101
  const width = Vector.zeros(n);
@@ -113,6 +134,6 @@
113
134
  </script>
114
135
 
115
136
  {#if isOk}
116
- <Rectangles className="series series-bar" left={x.left} width={x.width} top={y.top} height={y.height}
137
+ <Rectangles className="series-bar" left={x.left} width={x.width} top={y.top} height={y.height}
117
138
  lineWidth={lineWidth} {lineColor} {faceColor} {onclick} />
118
139
  {/if}
@@ -1,5 +1,5 @@
1
1
  <!--
2
- @component Adds a heatmap visializing values of a matrix.
2
+ @component Adds a heatmap visualizing values of a matrix.
3
3
 
4
4
  Main properties:
5
5
  - `values` - matrix (object of class `Matrix` from `mdatools` package) with values to visualize.
@@ -13,7 +13,7 @@
13
13
  import { Matrix } from 'mdatools/arrays';
14
14
  import { Axes, Heatmap } from 'svelte-plots-basic/2d';
15
15
 
16
- // create matrix with 5 rows and 10 columns filled with 5random values.
16
+ // create matrix with 5 rows and 10 columns filled with random values.
17
17
  const x = Matrix.rand(5, 10);
18
18
  </script>
19
19
 
@@ -91,7 +91,14 @@
91
91
 
92
92
 
93
93
  // check that values are provided as a matrix
94
- let v = $derived(ismatrix(values) && values.nrows > 0 && values.ncols > 0 ? values : null);
94
+ const v = $derived.by(() => {
95
+ if (!ismatrix(values) || values.nrows < 1 || values.ncols < 1) {
96
+ console.error('Heatmap: parameter "values" must be a non-empty Matrix.');
97
+ return null;
98
+ }
99
+
100
+ return values;
101
+ });
95
102
 
96
103
  // check and process left values for breaks
97
104
  let lb = $derived.by(() => {
@@ -121,9 +128,11 @@
121
128
  let lc = $derived.by(() => {
122
129
  if (!lb) return null;
123
130
 
124
- if (colmap) {
125
- if (!Array.isArray(colmap)) {
126
- console.error('Heatmap: parameter "colmap" must be array with colors.');
131
+ if (colmap !== null && colmap !== undefined) {
132
+ if (!Array.isArray(colmap) || colmap.some(color =>
133
+ typeof color !== 'string' || color.trim() === ''
134
+ )) {
135
+ console.error('Heatmap: parameter "colmap" must be an array of non-empty color strings.');
127
136
  return null;
128
137
  }
129
138
  if (colmap.length !== lb.length - 1) {