@takaro/lib-components 0.0.29 → 0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takaro/lib-components",
3
- "version": "0.0.29",
3
+ "version": "0.1.0",
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",
@@ -3,7 +3,7 @@
3
3
  exports[`Should render <Button/> 1`] = `
4
4
  <div>
5
5
  <button
6
- class="sc-jlGgGc iqEzFy"
6
+ class="sc-cKXybt hgqaEX"
7
7
  color="primary"
8
8
  tabindex="0"
9
9
  type="button"
@@ -4,7 +4,7 @@ exports[`Should render <IconButton /> 1`] = `
4
4
  <div>
5
5
  <button
6
6
  aria-label="test"
7
- class="sc-lmUcrn bKlaHv"
7
+ class="sc-cgjDci duTlkQ"
8
8
  color="primary"
9
9
  type="button"
10
10
  >
@@ -31,7 +31,6 @@ export interface AreaChartProps<T> extends ChartProps {
31
31
  brushMargin?: Margin;
32
32
  }
33
33
 
34
- // eslint-disable-next-line quotes
35
34
  const formatDate = timeFormat("%b %d, '%y");
36
35
 
37
36
  const defaultMargin = { top: 20, left: 50, bottom: 20, right: 5 };
@@ -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
- const centerX = width / 2;
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(width, height) * 0.25;
263
+ const scale = Math.min(mapWidth, height) * 0.25;
90
264
 
91
265
  const colorScale = scaleLinear({
92
- domain: [Math.min(...data.map((d) => yAccessor(d))), Math.max(...data.map((d) => yAccessor(d)))],
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={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={width} height={height} fill={theme.colors.background} rx={10} />
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={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={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
- <div style={{ position: 'relative' }}>
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
  };
@@ -19,7 +19,6 @@ const defaultShowAxisX = true;
19
19
  const defaultShowAxisY = true;
20
20
 
21
21
  type CurveType = keyof typeof allCurves;
22
- // eslint-disable-next-line quotes
23
22
  const formatDate = timeFormat("%b %d, '%y");
24
23
 
25
24
  export interface LineChartProps<T> extends ChartProps {
@@ -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"