@takaro/lib-components 0.0.30 → 0.1.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.
- package/package.json +1 -1
- package/src/components/actions/Button/__snapshots__/Button.test.tsx.snap +1 -1
- package/src/components/actions/IconButton/__snapshots__/IconButton.test.tsx.snap +1 -1
- package/src/components/charts/AreaChart/index.tsx +0 -1
- package/src/components/charts/GeoMercator/index.tsx +198 -42
- package/src/components/charts/LineChart/index.tsx +0 -1
- package/src/components/other/Plan/index.tsx +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takaro/lib-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Takaro UI is a simple and customizable component library to build React apps faster within the Takaro eco system",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -7,12 +7,176 @@ import { ParentSize } from '@visx/responsive';
|
|
|
7
7
|
import { getDefaultTooltipStyles, InnerChartProps, Margin } from '../util';
|
|
8
8
|
import { useTheme } from '../../../hooks';
|
|
9
9
|
import { useTooltip, Tooltip } from '@visx/tooltip';
|
|
10
|
+
import { styled } from '../../../styled';
|
|
10
11
|
import { Zoom } from '@visx/zoom';
|
|
11
12
|
import { useCallback } from 'react';
|
|
12
13
|
import { localPoint } from '@visx/event';
|
|
13
14
|
import { ZoomControls } from '../ZoomControls';
|
|
14
15
|
import { alpha2ToAlpha3 } from './iso3166-alpha2-to-alpha3';
|
|
15
16
|
|
|
17
|
+
const getCountryFlag = (countryCode: string): string => {
|
|
18
|
+
if (!countryCode || countryCode.length !== 2) {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const codePoints = countryCode
|
|
23
|
+
.toUpperCase()
|
|
24
|
+
.split('')
|
|
25
|
+
.map((char) => 127397 + char.charCodeAt(0));
|
|
26
|
+
|
|
27
|
+
return String.fromCodePoint(...codePoints);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const alpha3ToAlpha2: Record<string, string> = Object.entries(alpha2ToAlpha3).reduce(
|
|
31
|
+
(acc, [alpha2, alpha3]) => {
|
|
32
|
+
acc[alpha3] = alpha2;
|
|
33
|
+
return acc;
|
|
34
|
+
},
|
|
35
|
+
{} as Record<string, string>,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const SidebarContainer = styled.div`
|
|
39
|
+
width: 320px;
|
|
40
|
+
min-width: 320px;
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
height: 100%;
|
|
43
|
+
overflow: auto;
|
|
44
|
+
background-color: ${({ theme }) => theme.colors.backgroundAlt};
|
|
45
|
+
border: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
|
|
46
|
+
border-radius: ${({ theme }) => theme.borderRadius.medium};
|
|
47
|
+
padding: ${({ theme }) => theme.spacing['3']};
|
|
48
|
+
margin-left: ${({ theme }) => theme.spacing['4']};
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const SidebarTitle = styled.h3`
|
|
52
|
+
margin: 0 0 ${({ theme }) => theme.spacing['2']} 0;
|
|
53
|
+
font-size: ${({ theme }) => theme.fontSize.tiny};
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
color: ${({ theme }) => theme.colors.textAlt};
|
|
56
|
+
text-transform: uppercase;
|
|
57
|
+
letter-spacing: 0.5px;
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const CountryGrid = styled.div`
|
|
61
|
+
display: grid;
|
|
62
|
+
grid-template-columns: 1fr 1fr;
|
|
63
|
+
gap: ${({ theme }) => theme.spacing['2']};
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const CountryTable = styled.div`
|
|
67
|
+
display: table;
|
|
68
|
+
width: 100%;
|
|
69
|
+
font-size: ${({ theme }) => theme.fontSize.small};
|
|
70
|
+
border-collapse: collapse;
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const CountryRow = styled.div`
|
|
74
|
+
display: table-row;
|
|
75
|
+
|
|
76
|
+
&:hover {
|
|
77
|
+
background-color: ${({ theme }) => theme.colors.backgroundAccent};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&:last-child > div {
|
|
81
|
+
border-bottom: none;
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
const CountryCell = styled.div`
|
|
86
|
+
display: table-cell;
|
|
87
|
+
padding: 2px 0;
|
|
88
|
+
vertical-align: middle;
|
|
89
|
+
border-bottom: 1px solid ${({ theme }) => theme.colors.backgroundAccent};
|
|
90
|
+
color: ${({ theme }) => theme.colors.text};
|
|
91
|
+
line-height: 1.3;
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
const FlagCell = styled(CountryCell)`
|
|
95
|
+
width: 16px;
|
|
96
|
+
text-align: center;
|
|
97
|
+
font-size: 10px;
|
|
98
|
+
padding-right: 4px;
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
const CountryCell2 = styled(CountryCell)`
|
|
102
|
+
padding-right: 4px;
|
|
103
|
+
font-weight: 500;
|
|
104
|
+
font-size: ${({ theme }) => theme.fontSize.tiny};
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
const CountCell = styled(CountryCell)`
|
|
108
|
+
text-align: right;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
color: ${({ theme }) => theme.colors.primary};
|
|
111
|
+
width: 20px;
|
|
112
|
+
font-size: ${({ theme }) => theme.fontSize.tiny};
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
const FlexContainer = styled.div`
|
|
116
|
+
display: flex;
|
|
117
|
+
flex-direction: row;
|
|
118
|
+
align-items: stretch;
|
|
119
|
+
position: relative;
|
|
120
|
+
height: 100%;
|
|
121
|
+
width: 100%;
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
const MapContainer = styled.div`
|
|
125
|
+
flex: 1;
|
|
126
|
+
position: relative;
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
const StyledTooltip = styled(Tooltip)`
|
|
130
|
+
text-align: center;
|
|
131
|
+
transform: translate(-50%);
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
interface CountrySidebarProps<T> {
|
|
135
|
+
data: T[];
|
|
136
|
+
xAccessor: (d: T) => string;
|
|
137
|
+
yAccessor: (d: T) => number;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const CountrySidebar = <T,>({ data, xAccessor, yAccessor }: CountrySidebarProps<T>) => {
|
|
141
|
+
const sortedData = [...data].sort((a, b) => yAccessor(b) - yAccessor(a));
|
|
142
|
+
|
|
143
|
+
// Split data into two columns
|
|
144
|
+
const midPoint = Math.ceil(sortedData.length / 2);
|
|
145
|
+
const leftColumnData = sortedData.slice(0, midPoint);
|
|
146
|
+
const rightColumnData = sortedData.slice(midPoint);
|
|
147
|
+
|
|
148
|
+
const renderCountryTable = (columnData: T[]) => (
|
|
149
|
+
<CountryTable>
|
|
150
|
+
{columnData.map((item, index) => {
|
|
151
|
+
const countryCode = xAccessor(item);
|
|
152
|
+
const playerCount = yAccessor(item);
|
|
153
|
+
const alpha2Code = countryCode.length === 3 ? alpha3ToAlpha2[countryCode] : countryCode;
|
|
154
|
+
const flag = getCountryFlag(alpha2Code);
|
|
155
|
+
// Prefer 2-letter country codes for display if available
|
|
156
|
+
const displayCode = alpha2Code || countryCode;
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<CountryRow key={`${countryCode}-${index}`}>
|
|
160
|
+
<FlagCell>{flag}</FlagCell>
|
|
161
|
+
<CountryCell2>{displayCode}</CountryCell2>
|
|
162
|
+
<CountCell>{playerCount}</CountCell>
|
|
163
|
+
</CountryRow>
|
|
164
|
+
);
|
|
165
|
+
})}
|
|
166
|
+
</CountryTable>
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<SidebarContainer>
|
|
171
|
+
<SidebarTitle>Countries ({sortedData.length})</SidebarTitle>
|
|
172
|
+
<CountryGrid>
|
|
173
|
+
{renderCountryTable(leftColumnData)}
|
|
174
|
+
{renderCountryTable(rightColumnData)}
|
|
175
|
+
</CountryGrid>
|
|
176
|
+
</SidebarContainer>
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
|
|
16
180
|
interface FeatureShape {
|
|
17
181
|
type: 'Feature';
|
|
18
182
|
id: string;
|
|
@@ -36,6 +200,7 @@ export interface GeoMercatorProps<T> {
|
|
|
36
200
|
tooltipAccessor?: (d: T) => string;
|
|
37
201
|
showZoomControls?: boolean;
|
|
38
202
|
allowZoomAndDrag?: boolean;
|
|
203
|
+
showCountrySidebar?: boolean;
|
|
39
204
|
}
|
|
40
205
|
|
|
41
206
|
type InnerGeoMercator<T> = InnerChartProps & GeoMercatorProps<T>;
|
|
@@ -49,6 +214,7 @@ export const GeoMercator = <T,>({
|
|
|
49
214
|
tooltipAccessor,
|
|
50
215
|
showZoomControls = false,
|
|
51
216
|
allowZoomAndDrag = false,
|
|
217
|
+
showCountrySidebar = false,
|
|
52
218
|
}: GeoMercatorProps<T>) => {
|
|
53
219
|
return (
|
|
54
220
|
<ParentSize>
|
|
@@ -64,6 +230,7 @@ export const GeoMercator = <T,>({
|
|
|
64
230
|
allowZoomAndDrag={allowZoomAndDrag}
|
|
65
231
|
showZoomControls={showZoomControls}
|
|
66
232
|
tooltipAccessor={tooltipAccessor}
|
|
233
|
+
showCountrySidebar={showCountrySidebar}
|
|
67
234
|
/>
|
|
68
235
|
)}
|
|
69
236
|
</ParentSize>
|
|
@@ -80,16 +247,23 @@ const Chart = <T,>({
|
|
|
80
247
|
name,
|
|
81
248
|
allowZoomAndDrag,
|
|
82
249
|
showZoomControls,
|
|
250
|
+
showCountrySidebar,
|
|
83
251
|
}: InnerGeoMercator<T>) => {
|
|
84
252
|
const theme = useTheme();
|
|
85
253
|
const { hideTooltip, showTooltip, tooltipData, tooltipLeft = 0, tooltipTop = 0 } = useTooltip<T>();
|
|
86
254
|
|
|
87
|
-
|
|
255
|
+
// Calculate sidebar width including margin
|
|
256
|
+
const sidebarTotalWidth = showCountrySidebar ? 320 + parseInt(theme.spacing['4']) : 0;
|
|
257
|
+
|
|
258
|
+
// Adjust map width to account for sidebar
|
|
259
|
+
const mapWidth = width - sidebarTotalWidth;
|
|
260
|
+
|
|
261
|
+
const centerX = mapWidth / 2;
|
|
88
262
|
const centerY = height / 2;
|
|
89
|
-
const scale = Math.min(
|
|
263
|
+
const scale = Math.min(mapWidth, height) * 0.25;
|
|
90
264
|
|
|
91
265
|
const colorScale = scaleLinear({
|
|
92
|
-
domain: [
|
|
266
|
+
domain: [0, Math.max(...data.map((d) => yAccessor(d)))],
|
|
93
267
|
range: [theme.colors.backgroundAlt, theme.colors.primary],
|
|
94
268
|
});
|
|
95
269
|
|
|
@@ -117,7 +291,7 @@ const Chart = <T,>({
|
|
|
117
291
|
<svg
|
|
118
292
|
id={name}
|
|
119
293
|
name={name}
|
|
120
|
-
width={
|
|
294
|
+
width={mapWidth}
|
|
121
295
|
height={height}
|
|
122
296
|
ref={zoom?.containerRef}
|
|
123
297
|
style={{
|
|
@@ -125,7 +299,7 @@ const Chart = <T,>({
|
|
|
125
299
|
cursor: allowZoomAndDrag && zoom ? (zoom.isDragging ? 'grabbing' : 'grab') : 'default',
|
|
126
300
|
}}
|
|
127
301
|
>
|
|
128
|
-
<rect x={0} y={0} width={
|
|
302
|
+
<rect x={0} y={0} width={mapWidth} height={height} fill={theme.colors.background} rx={10} />
|
|
129
303
|
<Mercator<FeatureShape>
|
|
130
304
|
data={world.features}
|
|
131
305
|
scale={zoom?.transformMatrix.scaleX || scale}
|
|
@@ -167,7 +341,7 @@ const Chart = <T,>({
|
|
|
167
341
|
<rect
|
|
168
342
|
x={0}
|
|
169
343
|
y={0}
|
|
170
|
-
width={
|
|
344
|
+
width={mapWidth}
|
|
171
345
|
height={height}
|
|
172
346
|
rx={14}
|
|
173
347
|
fill="transparent"
|
|
@@ -185,9 +359,24 @@ const Chart = <T,>({
|
|
|
185
359
|
</svg>
|
|
186
360
|
);
|
|
187
361
|
|
|
362
|
+
const renderContent = (zoomProps?: any) => (
|
|
363
|
+
<FlexContainer>
|
|
364
|
+
<MapContainer>
|
|
365
|
+
{renderMap(zoomProps)}
|
|
366
|
+
{showZoomControls && zoomProps && <ZoomControls zoom={zoomProps} />}
|
|
367
|
+
{tooltipData && (
|
|
368
|
+
<StyledTooltip top={tooltipTop} left={tooltipLeft} style={getDefaultTooltipStyles(theme)}>
|
|
369
|
+
{tooltipAccessor ? tooltipAccessor(tooltipData) : `${xAccessor(tooltipData)}: ${yAccessor(tooltipData)}`}
|
|
370
|
+
</StyledTooltip>
|
|
371
|
+
)}
|
|
372
|
+
</MapContainer>
|
|
373
|
+
{showCountrySidebar && <CountrySidebar data={data} xAccessor={xAccessor} yAccessor={yAccessor} />}
|
|
374
|
+
</FlexContainer>
|
|
375
|
+
);
|
|
376
|
+
|
|
188
377
|
return allowZoomAndDrag ? (
|
|
189
378
|
<Zoom<SVGSVGElement>
|
|
190
|
-
width={
|
|
379
|
+
width={mapWidth}
|
|
191
380
|
height={height}
|
|
192
381
|
scaleXMin={100}
|
|
193
382
|
scaleXMax={1000}
|
|
@@ -202,42 +391,9 @@ const Chart = <T,>({
|
|
|
202
391
|
skewY: 0,
|
|
203
392
|
}}
|
|
204
393
|
>
|
|
205
|
-
{(zoom) => (
|
|
206
|
-
<div style={{ position: 'relative' }}>
|
|
207
|
-
{renderMap(zoom)}
|
|
208
|
-
{showZoomControls && <ZoomControls zoom={zoom} />}
|
|
209
|
-
{tooltipData && (
|
|
210
|
-
<Tooltip
|
|
211
|
-
top={tooltipTop}
|
|
212
|
-
left={tooltipLeft}
|
|
213
|
-
style={{
|
|
214
|
-
...getDefaultTooltipStyles(theme),
|
|
215
|
-
textAlign: 'center',
|
|
216
|
-
transform: 'translate(-50%)',
|
|
217
|
-
}}
|
|
218
|
-
>
|
|
219
|
-
{tooltipAccessor ? tooltipAccessor(tooltipData) : `${xAccessor(tooltipData)}: ${yAccessor(tooltipData)}`}
|
|
220
|
-
</Tooltip>
|
|
221
|
-
)}
|
|
222
|
-
</div>
|
|
223
|
-
)}
|
|
394
|
+
{(zoom) => renderContent(zoom)}
|
|
224
395
|
</Zoom>
|
|
225
396
|
) : (
|
|
226
|
-
|
|
227
|
-
{renderMap()}
|
|
228
|
-
{tooltipData && (
|
|
229
|
-
<Tooltip
|
|
230
|
-
top={tooltipTop}
|
|
231
|
-
left={tooltipLeft}
|
|
232
|
-
style={{
|
|
233
|
-
...getDefaultTooltipStyles(theme),
|
|
234
|
-
textAlign: 'center',
|
|
235
|
-
transform: 'translate(-50%)',
|
|
236
|
-
}}
|
|
237
|
-
>
|
|
238
|
-
{tooltipAccessor ? tooltipAccessor(tooltipData) : `${xAccessor(tooltipData)}: ${yAccessor(tooltipData)}`}
|
|
239
|
-
</Tooltip>
|
|
240
|
-
)}
|
|
241
|
-
</div>
|
|
397
|
+
renderContent()
|
|
242
398
|
);
|
|
243
399
|
};
|
|
@@ -46,7 +46,6 @@ export const Plan: FC<PlanProps> = ({
|
|
|
46
46
|
</h1>
|
|
47
47
|
<p style={{ marginBottom: '3rem' }}>{description}</p>
|
|
48
48
|
<Divider
|
|
49
|
-
// eslint-disable-next-line quotes
|
|
50
49
|
label={{ text: "What's included", labelPosition: 'center', color: 'primary' }}
|
|
51
50
|
fullWidth
|
|
52
51
|
size="large"
|