@takaro/lib-components 0.4.9 → 0.4.10

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.
Files changed (57) hide show
  1. package/package.json +1 -1
  2. package/src/components/actions/Button/__snapshots__/Button.test.tsx.snap +1 -1
  3. package/src/components/actions/IconButton/__snapshots__/IconButton.test.tsx.snap +1 -1
  4. package/src/components/charts/AreaChart/AreaChart.stories.tsx +11 -7
  5. package/src/components/charts/AreaChart/index.tsx +114 -63
  6. package/src/components/charts/BarChart/BarChart.stories.tsx +33 -10
  7. package/src/components/charts/BarChart/index.tsx +280 -147
  8. package/src/components/charts/EmptyChart.tsx +45 -0
  9. package/src/components/charts/GeoMercator/GeoMercator.stories.tsx +15 -9
  10. package/src/components/charts/GeoMercator/index.tsx +15 -172
  11. package/src/components/charts/Heatmap/Heatmap.stories.tsx +167 -33
  12. package/src/components/charts/Heatmap/index.tsx +427 -193
  13. package/src/components/charts/LineChart/LineChart.stories.tsx +77 -3
  14. package/src/components/charts/LineChart/index.tsx +200 -79
  15. package/src/components/charts/PieChart/PieChart.stories.tsx +128 -20
  16. package/src/components/charts/PieChart/index.tsx +353 -59
  17. package/src/components/charts/PointHighlight.tsx +2 -2
  18. package/src/components/charts/RadarChart/RadarChart.stories.tsx +14 -5
  19. package/src/components/charts/RadarChart/index.tsx +94 -45
  20. package/src/components/charts/RadialBarChart/RadialBarChart.stories.tsx +26 -1
  21. package/src/components/charts/RadialBarChart/index.tsx +100 -34
  22. package/src/components/charts/RadialLineChart/RadialLineChart.stories.tsx +19 -2
  23. package/src/components/charts/RadialLineChart/index.tsx +116 -26
  24. package/src/components/charts/index.tsx +0 -26
  25. package/src/components/charts/util.ts +50 -12
  26. package/src/components/data/CountryList/index.tsx +146 -0
  27. package/src/components/data/Stats/Sparkline.tsx +48 -0
  28. package/src/components/data/Stats/Stat.tsx +15 -4
  29. package/src/components/data/Stats/context.tsx +1 -1
  30. package/src/components/data/Stats/index.tsx +8 -3
  31. package/src/components/data/index.ts +3 -0
  32. package/src/components/feedback/IconTooltip/index.tsx +9 -6
  33. package/src/components/feedback/ProgressBar/ProgressBar.stories.tsx +13 -14
  34. package/src/components/feedback/ProgressBar/index.tsx +1 -1
  35. package/src/components/inputs/DurationField/__tests__/Generic.test.tsx +12 -0
  36. package/src/components/visual/Card/CardTitle.tsx +7 -1
  37. package/src/components/visual/Card/index.tsx +0 -4
  38. package/src/helpers/formatNumber.ts +6 -0
  39. package/src/helpers/index.ts +1 -0
  40. package/src/components/charts/echarts/EChartsArea.stories.tsx +0 -139
  41. package/src/components/charts/echarts/EChartsArea.tsx +0 -139
  42. package/src/components/charts/echarts/EChartsBar.stories.tsx +0 -141
  43. package/src/components/charts/echarts/EChartsBar.tsx +0 -133
  44. package/src/components/charts/echarts/EChartsBase.tsx +0 -264
  45. package/src/components/charts/echarts/EChartsFunnel.stories.tsx +0 -164
  46. package/src/components/charts/echarts/EChartsFunnel.tsx +0 -114
  47. package/src/components/charts/echarts/EChartsHeatmap.stories.tsx +0 -168
  48. package/src/components/charts/echarts/EChartsHeatmap.tsx +0 -141
  49. package/src/components/charts/echarts/EChartsLine.stories.tsx +0 -132
  50. package/src/components/charts/echarts/EChartsLine.tsx +0 -111
  51. package/src/components/charts/echarts/EChartsPie.stories.tsx +0 -131
  52. package/src/components/charts/echarts/EChartsPie.tsx +0 -124
  53. package/src/components/charts/echarts/EChartsRadialBar.stories.tsx +0 -124
  54. package/src/components/charts/echarts/EChartsRadialBar.tsx +0 -118
  55. package/src/components/charts/echarts/EChartsScatter.stories.tsx +0 -166
  56. package/src/components/charts/echarts/EChartsScatter.tsx +0 -135
  57. package/src/components/charts/echarts/index.ts +0 -26
@@ -1,3 +1,5 @@
1
+ import { MouseEvent, useCallback } from 'react';
2
+
1
3
  import { ParentSize } from '@visx/responsive';
2
4
  import { Group } from '@visx/group';
3
5
  import { LineRadial } from '@visx/shape';
@@ -7,19 +9,22 @@ import { AxisLeft } from '@visx/axis';
7
9
  import { motion } from 'framer-motion';
8
10
  import { extent } from '@visx/vendor/d3-array';
9
11
  import { curveBasisOpen } from '@visx/curve';
12
+ import { useTooltipInPortal, useTooltip } from '@visx/tooltip';
13
+ import { localPoint } from '@visx/event';
10
14
 
11
- import { InnerChartProps, Margin } from '../util';
15
+ import { InnerChartProps, getDefaultTooltipStyles, TooltipConfig, ChartProps } from '../util';
12
16
  import { useTheme } from '../../../hooks';
13
17
  import { useGradients } from '../useGradients';
18
+ import { EmptyChart } from '../EmptyChart';
14
19
 
15
20
  const formatTicks = (val: NumberLike) => String(val);
16
21
 
17
- export interface RadialLineChartProps<T> {
18
- name: string;
22
+ export interface RadialLineChartProps<T> extends ChartProps {
19
23
  data: T[];
20
- margin?: Margin;
21
24
  xAccessor: (d: T) => number;
22
25
  yAccessor: (d: T) => number;
26
+ /** Tooltip configuration */
27
+ tooltip?: TooltipConfig<T>;
23
28
  }
24
29
 
25
30
  const defaultMargin = { top: 10, right: 0, bottom: 25, left: 40 };
@@ -29,21 +34,29 @@ export const RadialLineChart = <T,>({
29
34
  xAccessor,
30
35
  name,
31
36
  margin = defaultMargin,
37
+ animate = true,
38
+ tooltip,
32
39
  }: RadialLineChartProps<T>) => {
40
+ const hasData = data && data.length > 0;
41
+
33
42
  return (
34
43
  <>
35
44
  <ParentSize>
36
- {(parent) => (
37
- <Chart<T>
38
- name={name}
39
- data={data}
40
- width={parent.width}
41
- height={parent.height}
42
- margin={margin}
43
- yAccessor={yAccessor}
44
- xAccessor={xAccessor}
45
- />
46
- )}
45
+ {hasData
46
+ ? (parent) => (
47
+ <Chart<T>
48
+ name={name}
49
+ data={data}
50
+ width={parent.width}
51
+ height={parent.height}
52
+ margin={margin}
53
+ yAccessor={yAccessor}
54
+ xAccessor={xAccessor}
55
+ tooltip={tooltip}
56
+ animate={animate}
57
+ />
58
+ )
59
+ : () => <EmptyChart />}
47
60
  </ParentSize>
48
61
  </>
49
62
  );
@@ -51,10 +64,26 @@ export const RadialLineChart = <T,>({
51
64
 
52
65
  type InnerRadialLineChartProps<T> = InnerChartProps & RadialLineChartProps<T>;
53
66
 
54
- const Chart = <T,>({ width, xAccessor, yAccessor, data, name, height }: InnerRadialLineChartProps<T>) => {
67
+ const Chart = <T,>({
68
+ width,
69
+ xAccessor,
70
+ yAccessor,
71
+ data,
72
+ name,
73
+ height,
74
+ tooltip,
75
+ animate = true,
76
+ }: InnerRadialLineChartProps<T>) => {
77
+ const tooltipAccessor = tooltip?.accessor;
55
78
  const theme = useTheme();
56
79
  const gradients = useGradients(name);
57
80
 
81
+ const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, hideTooltip, showTooltip } = useTooltip<T>();
82
+ const { containerRef, TooltipInPortal } = useTooltipInPortal({
83
+ detectBounds: true,
84
+ scroll: true,
85
+ });
86
+
58
87
  const xScale = scaleTime<number>({
59
88
  range: [0, Math.PI * 2],
60
89
  domain: extent(data, xAccessor) as [number, number],
@@ -64,19 +93,32 @@ const Chart = <T,>({ width, xAccessor, yAccessor, data, name, height }: InnerRad
64
93
  domain: extent(data, yAccessor) as [number, number],
65
94
  });
66
95
 
67
- const firstPoint = yAccessor(data[0]);
68
- const lastPoint = yAccessor(data[data.length - 1]);
69
-
70
96
  const angle = (d: T) => xScale(xAccessor(d)) ?? 0;
71
97
  const radius = (d: T) => yScale(yAccessor(d)) ?? 0;
72
98
  const padding = 15;
73
99
 
100
+ const handleMouseOver = useCallback(
101
+ (event: MouseEvent, dataPoint: T) => {
102
+ if (!tooltipAccessor) return;
103
+
104
+ const target = event.target as SVGElement;
105
+ const coords = localPoint(target.ownerSVGElement!, event);
106
+
107
+ showTooltip({
108
+ tooltipLeft: coords?.x,
109
+ tooltipTop: coords?.y,
110
+ tooltipData: dataPoint,
111
+ });
112
+ },
113
+ [tooltipAccessor, showTooltip],
114
+ );
115
+
74
116
  // Update scale output to match component dimensions
75
117
  yScale.range([0, height / 2 - padding]);
76
118
  const reverseYScale = yScale.copy().range(yScale.range().reverse());
77
119
 
78
120
  return width < 10 ? null : (
79
- <svg width={width} height={height}>
121
+ <svg ref={containerRef} width={width} height={height}>
80
122
  {gradients.chart.gradient}
81
123
  <rect width={width} height={height} fill={theme.colors.background} rx={14} />
82
124
  <Group top={height / 2} left={width / 2}>
@@ -121,21 +163,69 @@ const Chart = <T,>({ width, xAccessor, yAccessor, data, name, height }: InnerRad
121
163
  return (
122
164
  <motion.path
123
165
  d={d}
124
- strokeWidth={2}
166
+ strokeWidth={3}
125
167
  strokeOpacity={0.8}
126
168
  strokeLinecap="round"
127
169
  fill="none"
128
170
  stroke={theme.colors.primary}
171
+ initial={animate ? { pathLength: 0 } : { pathLength: 1 }}
172
+ animate={{ pathLength: 1 }}
173
+ transition={
174
+ animate
175
+ ? {
176
+ duration: 1.5,
177
+ ease: 'easeInOut',
178
+ }
179
+ : { duration: 0 }
180
+ }
129
181
  />
130
182
  );
131
183
  }}
132
184
  </LineRadial>
133
- {[firstPoint, lastPoint].map((d, i) => {
134
- const cx = ((xScale(d) ?? 0) * Math.PI) / 180;
135
- const cy = -(yScale(d) ?? 0);
136
- return <circle key={`line-cap-${i}`} cx={cx} cy={cy} fill={theme.colors.primary} r={2} />;
137
- })}
185
+ {tooltipAccessor &&
186
+ data.map((d, i) => {
187
+ const angleVal = angle(d);
188
+ const radiusVal = radius(d);
189
+ const x = Math.cos(angleVal - Math.PI / 2) * radiusVal;
190
+ const y = Math.sin(angleVal - Math.PI / 2) * radiusVal;
191
+
192
+ return (
193
+ <g key={`data-point-${i}`}>
194
+ {/* Invisible hit area for tooltip - no need for visible dots */}
195
+ <motion.circle
196
+ cx={x}
197
+ cy={y}
198
+ r={12}
199
+ fill="transparent"
200
+ style={{ cursor: 'pointer' }}
201
+ onMouseOut={hideTooltip}
202
+ onMouseOver={(e) => handleMouseOver(e, d)}
203
+ initial={animate ? { scale: 0 } : { scale: 1 }}
204
+ animate={{ scale: 1 }}
205
+ transition={
206
+ animate
207
+ ? {
208
+ duration: 0.3,
209
+ delay: 1.5,
210
+ ease: 'easeOut',
211
+ }
212
+ : { duration: 0 }
213
+ }
214
+ />
215
+ </g>
216
+ );
217
+ })}
138
218
  </Group>
219
+ {tooltipOpen && tooltipData && tooltipAccessor && (
220
+ <TooltipInPortal
221
+ key={`tooltip-${name}`}
222
+ top={tooltipTop}
223
+ left={tooltipLeft}
224
+ style={getDefaultTooltipStyles(theme)}
225
+ >
226
+ {tooltipAccessor(tooltipData)}
227
+ </TooltipInPortal>
228
+ )}
139
229
  </svg>
140
230
  );
141
231
  };
@@ -24,29 +24,3 @@ export type { RadialBarChartProps } from './RadialBarChart';
24
24
 
25
25
  export { GeoMercator } from './GeoMercator';
26
26
  export type { GeoMercatorProps } from './GeoMercator';
27
-
28
- // ECharts components
29
- export {
30
- EChartsBase,
31
- ResponsiveECharts,
32
- EChartsLine,
33
- EChartsBar,
34
- EChartsPie,
35
- EChartsArea,
36
- EChartsHeatmap,
37
- EChartsRadialBar,
38
- EChartsScatter,
39
- EChartsFunnel,
40
- } from './echarts';
41
-
42
- export type {
43
- EChartsBaseProps,
44
- EChartsLineProps,
45
- EChartsBarProps,
46
- EChartsPieProps,
47
- EChartsAreaProps,
48
- EChartsHeatmapProps,
49
- EChartsRadialBarProps,
50
- EChartsScatterProps,
51
- EChartsFunnelProps,
52
- } from './echarts';
@@ -1,14 +1,50 @@
1
1
  import { defaultStyles } from '@visx/tooltip';
2
2
  import { ThemeType } from '../../styled';
3
3
 
4
+ export interface Margin {
5
+ top: number;
6
+ right: number;
7
+ bottom: number;
8
+ left: number;
9
+ }
10
+
11
+ export interface AxisConfig {
12
+ showX?: boolean;
13
+ showY?: boolean;
14
+ labelX?: string;
15
+ labelY?: string;
16
+ numTicksX?: number;
17
+ numTicksY?: number;
18
+ /** Whether to include zero in the Y-axis domain. Default: false (auto-scale from min) */
19
+ includeZeroY?: boolean;
20
+ }
21
+
22
+ export interface TooltipConfig<T> {
23
+ enabled?: boolean;
24
+ accessor?: (d: T, ...additionalArgs: any[]) => string;
25
+ }
26
+
27
+ export interface BrushConfig {
28
+ enabled?: boolean;
29
+ margin?: Margin;
30
+ }
31
+
32
+ export type LegendPosition = 'top' | 'right' | 'bottom' | 'left' | 'none';
33
+
34
+ /** Grid display options for charts */
35
+ export type GridDisplay = 'none' | 'x' | 'y' | 'xy';
36
+
4
37
  export interface ChartProps {
5
38
  /// Unique identifier for the chart
6
39
  name: string;
7
- showGrid?: boolean;
8
- axisXLabel?: string;
9
- axisYLabel?: string;
10
- showAxisX?: boolean;
11
- showAxisY?: boolean;
40
+ /** Display grid lines. Options: 'none', 'x', 'y', 'xy'. Default: 'none' */
41
+ grid?: GridDisplay;
42
+ /** Axis configuration */
43
+ axis?: AxisConfig;
44
+ /** Enable or disable animations. Default: true */
45
+ animate?: boolean;
46
+ /** Chart margins */
47
+ margin?: Margin;
12
48
  }
13
49
 
14
50
  export interface InnerChartProps extends ChartProps {
@@ -16,13 +52,6 @@ export interface InnerChartProps extends ChartProps {
16
52
  height: number;
17
53
  }
18
54
 
19
- export interface Margin {
20
- top: number;
21
- right: number;
22
- bottom: number;
23
- left: number;
24
- }
25
-
26
55
  export const getDefaultTooltipStyles = (theme: ThemeType) => ({
27
56
  ...defaultStyles,
28
57
  background: theme.colors.background,
@@ -30,6 +59,7 @@ export const getDefaultTooltipStyles = (theme: ThemeType) => ({
30
59
  borderRadius: theme.borderRadius.small,
31
60
  color: theme.colors.text,
32
61
  fontSize: theme.fontSize.small,
62
+ whiteSpace: 'pre-wrap' as const,
33
63
  });
34
64
 
35
65
  export const getChartColors = (theme: ThemeType) => [
@@ -37,4 +67,12 @@ export const getChartColors = (theme: ThemeType) => [
37
67
  '#FFD700', // Gold for a contrasting yet harmonious color
38
68
  '#87CEEB', // Sky blue to complement the purple
39
69
  '#FF69B4', // Hot pink for a playful, vibrant look
70
+ '#32CD32', // Lime green for nature-inspired freshness
71
+ '#FF8C00', // Dark orange for warmth
72
+ '#9370DB', // Medium purple for variety
73
+ '#20B2AA', // Light sea green for coolness
74
+ '#FF6347', // Tomato red for emphasis
75
+ '#4682B4', // Steel blue for professionalism
76
+ '#DAA520', // Goldenrod for richness
77
+ '#8B4789', // Dark orchid for depth
40
78
  ];
@@ -0,0 +1,146 @@
1
+ import { styled } from '../../../styled';
2
+ import { Flag } from '../../visual/Flag';
3
+ import { alpha2ToAlpha3 } from '../../charts/GeoMercator/iso3166-alpha2-to-alpha3';
4
+
5
+ const alpha3ToAlpha2: Record<string, string> = Object.entries(alpha2ToAlpha3).reduce(
6
+ (acc, [alpha2, alpha3]) => {
7
+ acc[alpha3] = alpha2;
8
+ return acc;
9
+ },
10
+ {} as Record<string, string>,
11
+ );
12
+
13
+ const Container = styled.div`
14
+ width: 320px;
15
+ min-width: 320px;
16
+ flex-shrink: 0;
17
+ height: 100%;
18
+ overflow: auto;
19
+ background-color: ${({ theme }) => theme.colors.backgroundAlt};
20
+ border: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
21
+ border-radius: ${({ theme }) => theme.borderRadius.medium};
22
+ padding: ${({ theme }) => theme.spacing['3']};
23
+ `;
24
+
25
+ const Title = styled.h3`
26
+ margin: 0 0 ${({ theme }) => theme.spacing['2']} 0;
27
+ font-size: ${({ theme }) => theme.fontSize.tiny};
28
+ font-weight: 500;
29
+ color: ${({ theme }) => theme.colors.textAlt};
30
+ text-transform: uppercase;
31
+ letter-spacing: 0.5px;
32
+ `;
33
+
34
+ const CountryGrid = styled.div`
35
+ display: grid;
36
+ grid-template-columns: 1fr 1fr;
37
+ gap: ${({ theme }) => theme.spacing['2']};
38
+ `;
39
+
40
+ const CountryTable = styled.div`
41
+ display: table;
42
+ width: 100%;
43
+ font-size: ${({ theme }) => theme.fontSize.small};
44
+ border-collapse: collapse;
45
+ `;
46
+
47
+ const CountryRow = styled.div`
48
+ display: table-row;
49
+
50
+ &:hover {
51
+ background-color: ${({ theme }) => theme.colors.backgroundAccent};
52
+ }
53
+
54
+ &:last-child > div {
55
+ border-bottom: none;
56
+ }
57
+ `;
58
+
59
+ const CountryCell = styled.div`
60
+ display: table-cell;
61
+ padding: 2px 0;
62
+ vertical-align: middle;
63
+ border-bottom: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
64
+ color: ${({ theme }) => theme.colors.text};
65
+ line-height: 1.3;
66
+ `;
67
+
68
+ const FlagCell = styled(CountryCell)`
69
+ width: 20px;
70
+ text-align: center;
71
+ padding-right: 4px;
72
+ `;
73
+
74
+ const CountryCodeCell = styled(CountryCell)`
75
+ padding-right: 4px;
76
+ font-weight: 500;
77
+ font-size: ${({ theme }) => theme.fontSize.tiny};
78
+ `;
79
+
80
+ const CountCell = styled(CountryCell)`
81
+ text-align: right;
82
+ font-weight: 600;
83
+ color: ${({ theme }) => theme.colors.primary};
84
+ width: 20px;
85
+ font-size: ${({ theme }) => theme.fontSize.tiny};
86
+ `;
87
+
88
+ export interface CountryListProps<T> {
89
+ /** Array of data items containing country information */
90
+ data: T[];
91
+ /** Function to extract country code from data item (supports both alpha-2 and alpha-3 codes) */
92
+ xAccessor: (d: T) => string;
93
+ /** Function to extract numeric value from data item */
94
+ yAccessor: (d: T) => number;
95
+ /** Optional title for the list. Defaults to "Countries ({count})" */
96
+ title?: string;
97
+ }
98
+
99
+ /**
100
+ * CountryList displays a sorted list of countries with flags and numeric values in a two-column layout.
101
+ *
102
+ * Features:
103
+ * - Automatically sorts data by numeric value (descending)
104
+ * - Displays country flags when available
105
+ * - Supports both alpha-2 (US) and alpha-3 (USA) country codes
106
+ * - Two-column grid layout for compact display
107
+ * - Hover effects on rows
108
+ */
109
+ export const CountryList = <T,>({ data, xAccessor, yAccessor, title }: CountryListProps<T>) => {
110
+ const sortedData = [...data].sort((a, b) => yAccessor(b) - yAccessor(a));
111
+
112
+ // Split data into two columns
113
+ const midPoint = Math.ceil(sortedData.length / 2);
114
+ const leftColumnData = sortedData.slice(0, midPoint);
115
+ const rightColumnData = sortedData.slice(midPoint);
116
+
117
+ const renderCountryTable = (columnData: T[]) => (
118
+ <CountryTable>
119
+ {columnData.map((item, index) => {
120
+ const countryCode = xAccessor(item);
121
+ const count = yAccessor(item);
122
+ const alpha2Code = countryCode.length === 3 ? alpha3ToAlpha2[countryCode] : countryCode;
123
+ // Prefer 2-letter country codes for display if available
124
+ const displayCode = alpha2Code || countryCode;
125
+
126
+ return (
127
+ <CountryRow key={`${countryCode}-${index}`}>
128
+ <FlagCell>{alpha2Code && <Flag countryCode={alpha2Code} size={1} />}</FlagCell>
129
+ <CountryCodeCell>{displayCode}</CountryCodeCell>
130
+ <CountCell>{count}</CountCell>
131
+ </CountryRow>
132
+ );
133
+ })}
134
+ </CountryTable>
135
+ );
136
+
137
+ return (
138
+ <Container>
139
+ <Title>{title || `Countries (${sortedData.length})`}</Title>
140
+ <CountryGrid>
141
+ {renderCountryTable(leftColumnData)}
142
+ {renderCountryTable(rightColumnData)}
143
+ </CountryGrid>
144
+ </Container>
145
+ );
146
+ };
@@ -0,0 +1,48 @@
1
+ import { FC } from 'react';
2
+
3
+ export interface SparklineProps {
4
+ data: number[];
5
+ color?: string;
6
+ width?: string | number;
7
+ height?: string | number;
8
+ strokeWidth?: number;
9
+ }
10
+
11
+ /**
12
+ * Sparkline component - renders a simple SVG line chart for trend visualization
13
+ * Designed to be used as Stats.Sparkline for showing mini trend charts
14
+ */
15
+ export const Sparkline: FC<SparklineProps> = ({
16
+ data,
17
+ color = 'currentColor',
18
+ width = '100%',
19
+ height = '100%',
20
+ strokeWidth = 2,
21
+ }) => {
22
+ if (!data || data.length === 0) return null;
23
+
24
+ const max = Math.max(...data);
25
+ const min = Math.min(...data);
26
+ const range = max - min || 1;
27
+
28
+ const points = data
29
+ .map((value, index) => {
30
+ const divisor = Math.max(data.length - 1, 1); // in case of single data point
31
+ const x = (index / divisor) * 100;
32
+ const y = 100 - ((value - min) / range) * 100;
33
+ return `${x},${y}`;
34
+ })
35
+ .join(' ');
36
+
37
+ return (
38
+ <svg width={width} height={height} viewBox="0 0 100 100" preserveAspectRatio="none">
39
+ <polyline
40
+ points={points}
41
+ fill="none"
42
+ stroke={color}
43
+ strokeWidth={strokeWidth}
44
+ vectorEffect="non-scaling-stroke"
45
+ />
46
+ </svg>
47
+ );
48
+ };
@@ -1,18 +1,22 @@
1
1
  import { FC, useContext, ReactNode, cloneElement, isValidElement } from 'react';
2
- import { styled } from '../../../styled';
3
- import { StatContext, Direction, Size } from './context';
2
+ import { styled, Size } from '../../../styled';
3
+ import { StatContext, Direction } from './context';
4
4
  import { AiOutlineArrowUp, AiOutlineArrowDown } from 'react-icons/ai';
5
5
 
6
6
  const Container = styled.div<{ isGrouped: boolean; direction: Direction; size: Size }>`
7
7
  background-color: ${({ theme }) => theme.colors.backgroundAlt};
8
8
  padding: ${({ theme, size }) => {
9
9
  switch (size) {
10
+ case 'tiny':
11
+ return theme.spacing['0_5'];
10
12
  case 'small':
11
13
  return theme.spacing['1'];
12
14
  case 'medium':
13
15
  return theme.spacing['2'];
14
16
  case 'large':
15
17
  return theme.spacing['3'];
18
+ case 'huge':
19
+ return theme.spacing['4'];
16
20
  }
17
21
  }};
18
22
 
@@ -58,16 +62,19 @@ const Container = styled.div<{ isGrouped: boolean; direction: Direction; size: S
58
62
  dt {
59
63
  font-size: ${({ theme, size }) => {
60
64
  switch (size) {
65
+ case 'tiny':
66
+ return theme.fontSize.tiny;
61
67
  case 'small':
62
68
  return theme.fontSize.small;
63
69
  case 'medium':
64
70
  return theme.fontSize.medium;
65
71
  case 'large':
66
72
  return theme.fontSize.mediumLarge;
73
+ case 'huge':
74
+ return theme.fontSize.large;
67
75
  }
68
76
  }};
69
- color: ${({ theme }) => theme.colors.secondary};
70
- font-size: ${({ theme }) => theme.fontSize.medium};
77
+ color: ${({ theme }) => theme.colors.textAlt};
71
78
  margin-bottom: ${({ theme }) => theme.spacing['0_5']};
72
79
  }
73
80
 
@@ -76,12 +83,16 @@ const Container = styled.div<{ isGrouped: boolean; direction: Direction; size: S
76
83
  color: ${({ theme }) => theme.colors.white};
77
84
  font-size: ${({ theme, size }) => {
78
85
  switch (size) {
86
+ case 'tiny':
87
+ return theme.fontSize.small;
79
88
  case 'small':
80
89
  return theme.fontSize.medium;
81
90
  case 'medium':
82
91
  return theme.fontSize.mediumLarge;
83
92
  case 'large':
84
93
  return theme.fontSize.large;
94
+ case 'huge':
95
+ return theme.fontSize.huge;
85
96
  }
86
97
  }};
87
98
  color: ${({ theme }) => theme.colors.text};
@@ -1,7 +1,7 @@
1
1
  import { createContext } from 'react';
2
+ import { Size } from '../../../styled/types';
2
3
 
3
4
  export type Direction = 'horizontal' | 'vertical';
4
- export type Size = 'small' | 'medium' | 'large';
5
5
 
6
6
  interface StatsContextValue {
7
7
  grouped: boolean;
@@ -1,7 +1,8 @@
1
1
  import { FC, PropsWithChildren, Children, isValidElement } from 'react';
2
- import { styled } from '../../../styled';
2
+ import { styled, Size } from '../../../styled';
3
3
  import { Stat } from './Stat';
4
- import { StatContext, Direction, Size } from './context';
4
+ import { Sparkline } from './Sparkline';
5
+ import { StatContext, Direction } from './context';
5
6
 
6
7
  export const Container = styled.dl<{ direction: Direction; grouped: boolean; count: number }>`
7
8
  display: grid;
@@ -26,6 +27,7 @@ export interface StatsProps {
26
27
 
27
28
  interface SubComponents {
28
29
  Stat: typeof Stat;
30
+ Sparkline: typeof Sparkline;
29
31
  }
30
32
 
31
33
  export const Stats: FC<PropsWithChildren<StatsProps>> & SubComponents = ({
@@ -52,6 +54,9 @@ export const Stats: FC<PropsWithChildren<StatsProps>> & SubComponents = ({
52
54
  };
53
55
 
54
56
  Stats.Stat = Stat;
57
+ Stats.Sparkline = Sparkline;
55
58
 
56
- export type { Direction, Size } from './context';
59
+ export type { Direction } from './context';
60
+ export type { Size } from '../../../styled';
57
61
  export type { TrendConfig } from './Stat';
62
+ export type { SparklineProps } from './Sparkline';
@@ -23,3 +23,6 @@ export type { DateFormatterProps } from './DateFormatter';
23
23
 
24
24
  export { CopyId } from './CopyId';
25
25
  export type { CopyIdProps } from './CopyId';
26
+
27
+ export { CountryList } from './CountryList';
28
+ export type { CountryListProps } from './CountryList';
@@ -3,12 +3,11 @@ import { Tooltip, TooltipProps } from '../Tooltip';
3
3
  import { Size, styled } from '../../../styled';
4
4
  import { getIconSize } from '../../actions/IconButton/getIconSize';
5
5
  import { ButtonColor } from '../../actions/Button/style';
6
+ import { useTheme } from '../../../hooks';
6
7
 
7
- type TooltipIconColor = Exclude<ButtonColor, 'white'>;
8
+ type TooltipIconColor = ButtonColor;
8
9
 
9
- const TriggerContainer = styled.div<{ color: TooltipIconColor }>`
10
- border: 1px solid ${({ theme, color }) => theme.colors[color]};
11
- border-radius: ${({ theme }) => theme.borderRadius.small};
10
+ const TriggerContainer = styled.div`
12
11
  display: flex;
13
12
  align-items: center;
14
13
  justify-content: center;
@@ -33,12 +32,16 @@ export const IconTooltip: FC<IconTooltipProps> = ({
33
32
  open,
34
33
  icon,
35
34
  size = 'medium',
36
- color = 'primary',
35
+ color = 'white',
37
36
  }) => {
37
+ const theme = useTheme();
38
+
38
39
  return (
39
40
  <Tooltip initialOpen={initialOpen} open={open}>
40
41
  <Tooltip.Trigger asChild>
41
- <TriggerContainer color={color}>{cloneElement(icon, { size: getIconSize(size) })}</TriggerContainer>
42
+ <TriggerContainer>
43
+ {cloneElement(icon, { size: getIconSize(size), fill: theme.colors[color] })}
44
+ </TriggerContainer>
42
45
  </Tooltip.Trigger>
43
46
  <Tooltip.Content>
44
47
  <ContentContainer>{children}</ContentContainer>