exodeui-react-native 1.0.4 → 1.0.6
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/package.json +1 -1
- package/src/engine.ts +107 -76
package/package.json
CHANGED
package/src/engine.ts
CHANGED
|
@@ -1630,13 +1630,13 @@ export class ExodeUIEngine {
|
|
|
1630
1630
|
const paint = Skia.Paint();
|
|
1631
1631
|
|
|
1632
1632
|
const isPressed = this.draggingSliderId === obj.id;
|
|
1633
|
-
//
|
|
1634
|
-
const bgNormal = opts.backgroundColor || opts.background || '#3b82f6';
|
|
1633
|
+
// The .exode file uses 'buttonBgColor', fall back to 'backgroundColor'
|
|
1634
|
+
const bgNormal = opts.buttonBgColor || opts.backgroundColor || opts.background || '#3b82f6';
|
|
1635
1635
|
const bgActive = opts.activeBackgroundColor || opts.activeBackground || '#2563eb';
|
|
1636
|
-
const bgStr = typeof bgNormal === 'string' ? bgNormal : '#3b82f6';
|
|
1637
|
-
const bgActiveStr = typeof bgActive === 'string' ? bgActive : '#2563eb';
|
|
1636
|
+
const bgStr = (typeof bgNormal === 'string' ? bgNormal : '#3b82f6').replace(/\s+/g, '');
|
|
1637
|
+
const bgActiveStr = (typeof bgActive === 'string' ? bgActive : '#2563eb').replace(/\s+/g, '');
|
|
1638
1638
|
try {
|
|
1639
|
-
paint.setColor(Skia.Color(
|
|
1639
|
+
paint.setColor(Skia.Color(isPressed ? bgActiveStr : bgStr));
|
|
1640
1640
|
} catch {
|
|
1641
1641
|
paint.setColor(Skia.Color('#3b82f6'));
|
|
1642
1642
|
}
|
|
@@ -1644,7 +1644,8 @@ export class ExodeUIEngine {
|
|
|
1644
1644
|
const r = opts.cornerRadius ?? 8;
|
|
1645
1645
|
canvas.drawRRect(Skia.RRectXY({ x: -w/2, y: -h/2, width: w, height: h }, r, r), paint);
|
|
1646
1646
|
|
|
1647
|
-
|
|
1647
|
+
// label is an object {text, fontSize, color, ...}
|
|
1648
|
+
const labelObj = typeof opts.label === 'object' && opts.label !== null ? opts.label : {};
|
|
1648
1649
|
const labelText = labelObj.text || opts.text || opts.title || 'Button';
|
|
1649
1650
|
const fontSize = labelObj.fontSize || opts.fontSize || 14;
|
|
1650
1651
|
const labelColor = labelObj.color || opts.color || '#ffffff';
|
|
@@ -1658,8 +1659,7 @@ export class ExodeUIEngine {
|
|
|
1658
1659
|
}
|
|
1659
1660
|
if (isPressed) textPaint.setAlphaf(0.85);
|
|
1660
1661
|
|
|
1661
|
-
|
|
1662
|
-
const textWidth = font.getTextWidth ? font.getTextWidth(labelText) : 0;
|
|
1662
|
+
const textWidth = font.getTextWidth ? font.getTextWidth(String(labelText)) : 0;
|
|
1663
1663
|
canvas.save();
|
|
1664
1664
|
canvas.translate(-textWidth / 2, fontSize / 3);
|
|
1665
1665
|
canvas.drawText(String(labelText), 0, 0, textPaint, font);
|
|
@@ -1675,26 +1675,30 @@ export class ExodeUIEngine {
|
|
|
1675
1675
|
const trackColor = checked ? (opts.activeColor || '#3b82f6') : (opts.inactiveColor || '#374151');
|
|
1676
1676
|
try { paint.setColor(Skia.Color(trackColor.replace(/\s+/g, ''))); } catch { paint.setColor(Skia.Color('#374151')); }
|
|
1677
1677
|
|
|
1678
|
-
const r = h / 2;
|
|
1678
|
+
const r = opts.cornerRadius ?? h / 2;
|
|
1679
1679
|
canvas.drawRRect(Skia.RRectXY({ x: -w/2, y: -h/2, width: w, height: h }, r, r), paint);
|
|
1680
1680
|
|
|
1681
1681
|
const thumbPaint = Skia.Paint();
|
|
1682
|
-
|
|
1682
|
+
const thumbCol = opts.knobColor || opts.thumbColor || '#ffffff';
|
|
1683
|
+
try { thumbPaint.setColor(Skia.Color(thumbCol.replace(/\s+/g, ''))); } catch { thumbPaint.setColor(Skia.Color('#ffffff')); }
|
|
1683
1684
|
const thumbRadius = (h - 8) / 2;
|
|
1684
1685
|
const thumbX = checked ? (w / 2 - thumbRadius - 4) : (-w / 2 + thumbRadius + 4);
|
|
1685
1686
|
canvas.drawCircle(thumbX, 0, thumbRadius, thumbPaint);
|
|
1686
1687
|
|
|
1687
|
-
//
|
|
1688
|
-
const
|
|
1689
|
-
if (
|
|
1690
|
-
const fontSize =
|
|
1688
|
+
// label is an object: {text, position, fontSize, color, gap}
|
|
1689
|
+
const labelObj = typeof opts.label === 'object' && opts.label !== null ? opts.label : null;
|
|
1690
|
+
if (labelObj && labelObj.text) {
|
|
1691
|
+
const fontSize = labelObj.fontSize || 12;
|
|
1691
1692
|
const font = this.getFont(fontSize, 'Helvetica Neue');
|
|
1692
1693
|
const labelPaint = Skia.Paint();
|
|
1693
|
-
const labelColor =
|
|
1694
|
+
const labelColor = labelObj.color || '#ffffff';
|
|
1694
1695
|
try { labelPaint.setColor(Skia.Color((typeof labelColor === 'string' ? labelColor : '#ffffff').replace(/\s+/g, ''))); } catch { labelPaint.setColor(Skia.Color('#ffffff')); }
|
|
1696
|
+
const gap = labelObj.gap || 8;
|
|
1697
|
+
const position = labelObj.position || 'right';
|
|
1698
|
+
const offsetX = position === 'left' ? -(w/2 + gap + font.getTextWidth(String(labelObj.text))) : w/2 + gap;
|
|
1695
1699
|
canvas.save();
|
|
1696
|
-
canvas.translate(
|
|
1697
|
-
canvas.drawText(String(
|
|
1700
|
+
canvas.translate(offsetX, fontSize / 3);
|
|
1701
|
+
canvas.drawText(String(labelObj.text), 0, 0, labelPaint, font);
|
|
1698
1702
|
canvas.restore();
|
|
1699
1703
|
}
|
|
1700
1704
|
}
|
|
@@ -1714,7 +1718,7 @@ export class ExodeUIEngine {
|
|
|
1714
1718
|
canvas.drawRRect(Skia.RRectXY({ x: -w/2, y: -trackHeight/2, width: w, height: trackHeight }, trackHeight/2, trackHeight/2), trackPaint);
|
|
1715
1719
|
|
|
1716
1720
|
const activePaint = Skia.Paint();
|
|
1717
|
-
const activeColor = opts.activeColor ||
|
|
1721
|
+
const activeColor = opts.activeColor || '#3b82f6';
|
|
1718
1722
|
try { activePaint.setColor(Skia.Color((typeof activeColor === 'string' ? activeColor : '#3b82f6').replace(/\s+/g, ''))); } catch { activePaint.setColor(Skia.Color('#3b82f6')); }
|
|
1719
1723
|
const thumbWidth = opts.thumbWidth ?? 16;
|
|
1720
1724
|
const travelW = w - thumbWidth;
|
|
@@ -1727,16 +1731,39 @@ export class ExodeUIEngine {
|
|
|
1727
1731
|
try { thumbPaint.setColor(Skia.Color((typeof thumbCol === 'string' ? thumbCol : '#ffffff').replace(/\s+/g, ''))); } catch { thumbPaint.setColor(Skia.Color('#ffffff')); }
|
|
1728
1732
|
canvas.drawCircle(thumbX, 0, thumbWidth / 2, thumbPaint);
|
|
1729
1733
|
|
|
1730
|
-
//
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
const
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
const
|
|
1734
|
+
// Label: opts.label is an object {text, visible, fontSize, color}
|
|
1735
|
+
const labelObj = typeof opts.label === 'object' && opts.label !== null ? opts.label : null;
|
|
1736
|
+
if (labelObj && labelObj.text && labelObj.visible !== false) {
|
|
1737
|
+
const lFontSize = labelObj.fontSize || 12;
|
|
1738
|
+
const lFont = this.getFont(lFontSize, 'Helvetica Neue');
|
|
1739
|
+
const lPaint = Skia.Paint();
|
|
1740
|
+
const lColor = labelObj.color || '#ffffff';
|
|
1741
|
+
try { lPaint.setColor(Skia.Color((typeof lColor === 'string' ? lColor : '#9ca3af').replace(/\s+/g, ''))); } catch { lPaint.setColor(Skia.Color('#9ca3af')); }
|
|
1737
1742
|
canvas.save();
|
|
1738
|
-
canvas.translate(
|
|
1739
|
-
canvas.drawText(
|
|
1743
|
+
canvas.translate(-w/2, -h/2 - lFontSize - 2);
|
|
1744
|
+
canvas.drawText(String(labelObj.text), 0, 0, lPaint, lFont);
|
|
1745
|
+
canvas.restore();
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// Value tooltip: opts.showValueTooltip + tooltipOffsetY
|
|
1749
|
+
if (opts.showValueTooltip) {
|
|
1750
|
+
const tFontSize = 11;
|
|
1751
|
+
const tFont = this.getFont(tFontSize, 'Helvetica Neue');
|
|
1752
|
+
const tooltipBg = opts.tooltipBgColor || '#333333';
|
|
1753
|
+
const tooltipText = opts.tooltipColor || '#ffffff';
|
|
1754
|
+
const offsetY = opts.tooltipOffsetY ?? -25;
|
|
1755
|
+
const valStr = String(Math.round(value));
|
|
1756
|
+
const tWidth = tFont.getTextWidth ? tFont.getTextWidth(valStr) + 8 : 24;
|
|
1757
|
+
|
|
1758
|
+
const bgP = Skia.Paint();
|
|
1759
|
+
try { bgP.setColor(Skia.Color((typeof tooltipBg === 'string' ? tooltipBg : '#333333').replace(/\s+/g, ''))); } catch { bgP.setColor(Skia.Color('#333333')); }
|
|
1760
|
+
canvas.drawRRect(Skia.RRectXY({ x: thumbX - tWidth/2, y: offsetY - tFontSize - 2, width: tWidth, height: tFontSize + 6 }, 3, 3), bgP);
|
|
1761
|
+
|
|
1762
|
+
const tPaint = Skia.Paint();
|
|
1763
|
+
try { tPaint.setColor(Skia.Color((typeof tooltipText === 'string' ? tooltipText : '#ffffff').replace(/\s+/g, ''))); } catch { tPaint.setColor(Skia.Color('#ffffff')); }
|
|
1764
|
+
canvas.save();
|
|
1765
|
+
canvas.translate(thumbX - tWidth/2 + 4, offsetY - 4);
|
|
1766
|
+
canvas.drawText(valStr, 0, 0, tPaint, tFont);
|
|
1740
1767
|
canvas.restore();
|
|
1741
1768
|
}
|
|
1742
1769
|
}
|
|
@@ -2007,41 +2034,42 @@ export class ExodeUIEngine {
|
|
|
2007
2034
|
const data = ds.data || [];
|
|
2008
2035
|
if (data.length < 2) return;
|
|
2009
2036
|
|
|
2010
|
-
const lineColorStr =
|
|
2037
|
+
const lineColorStr = typeof ds.lineColor === 'string' ? ds.lineColor : '#3b82f6';
|
|
2038
|
+
// hsl/hsla colors need to be converted; Skia may not accept them directly
|
|
2039
|
+
// We'll try Skia.Color, and if it throws, fall back to a safe default
|
|
2011
2040
|
let lineColor;
|
|
2012
|
-
try { lineColor = Skia.Color(lineColorStr); } catch { lineColor = Skia.Color('#3b82f6'); }
|
|
2041
|
+
try { lineColor = Skia.Color(lineColorStr.replace(/\s+/g, '')); } catch { lineColor = Skia.Color('#3b82f6'); }
|
|
2013
2042
|
|
|
2014
|
-
const stepX = plotW / (data.length - 1);
|
|
2043
|
+
const stepX = plotW / (Math.max(data.length - 1, 1));
|
|
2015
2044
|
const getX = (i: number) => -w/2 + axisMarginL + i * stepX;
|
|
2016
2045
|
const getY = (val: number) => h/2 - axisMarginB - (val / globalMax) * plotH;
|
|
2017
2046
|
|
|
2018
|
-
|
|
2047
|
+
// Per-dataset smoothing from the .exode file
|
|
2048
|
+
const smooth = ds.smoothing === true;
|
|
2019
2049
|
|
|
2020
|
-
//
|
|
2021
|
-
if (ds.
|
|
2022
|
-
const
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
const
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
fillPath.lineTo(x, y);
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
try { fillPaint.setColor(lineColor); } catch { fillPaint.setColor(Skia.Color('#3b82f6')); }
|
|
2044
|
-
canvas.drawPath(fillPath, fillPaint);
|
|
2050
|
+
// Per-dataset area fill from .exode field 'showArea'
|
|
2051
|
+
if (ds.showArea === true && ds.areaColor) {
|
|
2052
|
+
const areaColorStr = typeof ds.areaColor === 'string' ? ds.areaColor : null;
|
|
2053
|
+
let areaColor;
|
|
2054
|
+
if (areaColorStr) {
|
|
2055
|
+
try { areaColor = Skia.Color(areaColorStr.replace(/\s+/g, '')); } catch { areaColor = null; }
|
|
2056
|
+
}
|
|
2057
|
+
if (areaColor) {
|
|
2058
|
+
const fillPath = Skia.Path.Make();
|
|
2059
|
+
fillPath.moveTo(getX(0), h/2 - axisMarginB);
|
|
2060
|
+
data.forEach((val: number, i: number) => {
|
|
2061
|
+
const x = getX(i); const y = getY(val);
|
|
2062
|
+
if (i === 0) fillPath.lineTo(x, y);
|
|
2063
|
+
else if (smooth) { const px = getX(i-1); const py = getY(data[i-1]); const cpX = (px+x)/2; fillPath.cubicTo(cpX, py, cpX, y, x, y); }
|
|
2064
|
+
else fillPath.lineTo(x, y);
|
|
2065
|
+
});
|
|
2066
|
+
fillPath.lineTo(getX(data.length - 1), h/2 - axisMarginB);
|
|
2067
|
+
fillPath.close();
|
|
2068
|
+
const fillPaint = Skia.Paint();
|
|
2069
|
+
fillPaint.setStyle(PaintStyle.Fill);
|
|
2070
|
+
fillPaint.setColor(areaColor);
|
|
2071
|
+
canvas.drawPath(fillPath, fillPaint);
|
|
2072
|
+
}
|
|
2045
2073
|
}
|
|
2046
2074
|
|
|
2047
2075
|
// Line
|
|
@@ -2052,26 +2080,29 @@ export class ExodeUIEngine {
|
|
|
2052
2080
|
|
|
2053
2081
|
const linePath = Skia.Path.Make();
|
|
2054
2082
|
data.forEach((val: number, i: number) => {
|
|
2055
|
-
const x = getX(i);
|
|
2056
|
-
const y = getY(val);
|
|
2083
|
+
const x = getX(i); const y = getY(val);
|
|
2057
2084
|
if (i === 0) linePath.moveTo(x, y);
|
|
2058
|
-
else if (smooth
|
|
2059
|
-
|
|
2060
|
-
const prevY = getY(data[i - 1]);
|
|
2061
|
-
const cpX = (prevX + x) / 2;
|
|
2062
|
-
linePath.cubicTo(cpX, prevY, cpX, y, x, y);
|
|
2063
|
-
} else {
|
|
2064
|
-
linePath.lineTo(x, y);
|
|
2065
|
-
}
|
|
2085
|
+
else if (smooth) { const px = getX(i-1); const py = getY(data[i-1]); const cpX = (px+x)/2; linePath.cubicTo(cpX, py, cpX, y, x, y); }
|
|
2086
|
+
else linePath.lineTo(x, y);
|
|
2066
2087
|
});
|
|
2067
2088
|
canvas.drawPath(linePath, linePaint);
|
|
2068
2089
|
|
|
2069
|
-
// Data points
|
|
2070
|
-
if (ds.showPoints !== false && data.length <=
|
|
2090
|
+
// Data points with custom point styling
|
|
2091
|
+
if (ds.showPoints !== false && data.length <= 30 && ds.pointSize) {
|
|
2092
|
+
const pr = ds.pointSize / 2;
|
|
2071
2093
|
data.forEach((val: number, i: number) => {
|
|
2094
|
+
// Outer stroke
|
|
2095
|
+
if (ds.pointStrokeColor) {
|
|
2096
|
+
const strokeP = Skia.Paint();
|
|
2097
|
+
strokeP.setStyle(PaintStyle.Stroke);
|
|
2098
|
+
strokeP.setStrokeWidth(ds.pointStrokeWidth || 2);
|
|
2099
|
+
try { strokeP.setColor(Skia.Color((ds.pointStrokeColor || '#3b82f6').replace(/\s+/g, ''))); } catch { strokeP.setColor(lineColor); }
|
|
2100
|
+
canvas.drawCircle(getX(i), getY(val), pr, strokeP);
|
|
2101
|
+
}
|
|
2102
|
+
// Fill
|
|
2072
2103
|
const dotPaint = Skia.Paint();
|
|
2073
|
-
try { dotPaint.setColor(
|
|
2074
|
-
canvas.drawCircle(getX(i), getY(val),
|
|
2104
|
+
try { dotPaint.setColor(Skia.Color((ds.pointFill || '#ffffff').replace(/\s+/g, ''))); } catch { dotPaint.setColor(Skia.Color('#ffffff')); }
|
|
2105
|
+
canvas.drawCircle(getX(i), getY(val), pr - (ds.pointStrokeWidth || 2)/2, dotPaint);
|
|
2075
2106
|
});
|
|
2076
2107
|
}
|
|
2077
2108
|
});
|
|
@@ -2107,15 +2138,15 @@ export class ExodeUIEngine {
|
|
|
2107
2138
|
});
|
|
2108
2139
|
}
|
|
2109
2140
|
|
|
2110
|
-
// Legend
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2141
|
+
// Legend — only if datasets have labels
|
|
2142
|
+
const datasetsWithLabels = datasets.filter((ds: any) => ds.label);
|
|
2143
|
+
if (datasetsWithLabels.length > 0) {
|
|
2144
|
+
datasetsWithLabels.forEach((ds: any, i: number) => {
|
|
2114
2145
|
const legX = -w/2 + axisMarginL + i * 80;
|
|
2115
2146
|
const legY = -h/2 + 10;
|
|
2116
2147
|
const legPaint = Skia.Paint();
|
|
2117
|
-
const lcStr =
|
|
2118
|
-
try { legPaint.setColor(Skia.Color(lcStr)); } catch { legPaint.setColor(Skia.Color('#3b82f6')); }
|
|
2148
|
+
const lcStr = typeof ds.lineColor === 'string' ? ds.lineColor : '#3b82f6';
|
|
2149
|
+
try { legPaint.setColor(Skia.Color(lcStr.replace(/\s+/g, ''))); } catch { legPaint.setColor(Skia.Color('#3b82f6')); }
|
|
2119
2150
|
canvas.drawRect({ x: legX, y: legY - 5, width: 16, height: 3 }, legPaint);
|
|
2120
2151
|
canvas.save();
|
|
2121
2152
|
canvas.translate(legX + 20, legY);
|