kyd-shared-badge 0.3.92 → 0.3.93
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 +2 -2
- package/src/components/SkillsBubble.tsx +65 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kyd-shared-badge",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.93",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"module": "./src/index.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@aws-sdk/lib-dynamodb": "^3.893.0",
|
|
22
22
|
"@chatscope/chat-ui-kit-react": "^2.1.1",
|
|
23
23
|
"@chatscope/chat-ui-kit-styles": "^1.4.0",
|
|
24
|
-
"@knowyourdeveloper/react-bubble-chart": "^1.0.
|
|
24
|
+
"@knowyourdeveloper/react-bubble-chart": "^1.0.5",
|
|
25
25
|
"@radix-ui/react-slot": "^1.2.3",
|
|
26
26
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
27
27
|
"ai": "5.0.47",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React, { useMemo, useRef, useState, useEffect } from 'react';
|
|
4
4
|
import { BubbleChart } from '@knowyourdeveloper/react-bubble-chart';
|
|
5
5
|
import '@knowyourdeveloper/react-bubble-chart/style.css';
|
|
6
|
+
import { green1, green2, green3, green4, green5 } from '../colors';
|
|
6
7
|
|
|
7
8
|
type SkillsRadarPoint = {
|
|
8
9
|
axis: string;
|
|
@@ -24,29 +25,37 @@ const TooltipBox = ({ state }: { state: HoverTooltipState }) => {
|
|
|
24
25
|
if (!state || !state.visible) return null;
|
|
25
26
|
return (
|
|
26
27
|
<div
|
|
28
|
+
className="rounded-md border shadow-sm px-3 py-2 text-xs"
|
|
27
29
|
style={{
|
|
28
30
|
position: 'absolute',
|
|
29
31
|
left: state.x,
|
|
30
32
|
top: state.y,
|
|
31
33
|
pointerEvents: 'none',
|
|
32
34
|
background: 'var(--content-card-background)',
|
|
33
|
-
|
|
35
|
+
borderColor: 'var(--icon-button-secondary)',
|
|
34
36
|
color: 'var(--text-main)',
|
|
35
|
-
|
|
36
|
-
borderRadius: 6,
|
|
37
|
+
zIndex: 10,
|
|
37
38
|
minWidth: 250,
|
|
38
39
|
maxWidth: 320,
|
|
39
|
-
zIndex: 10,
|
|
40
40
|
}}
|
|
41
41
|
>
|
|
42
|
-
<div style={{
|
|
42
|
+
<div className="font-medium" style={{ color: 'var(--text-main)' }}>{state.title}</div>
|
|
43
43
|
{state.body ? (
|
|
44
|
-
<div style={{
|
|
44
|
+
<div style={{ color: 'var(--text-secondary)' }}>{state.body}</div>
|
|
45
45
|
) : null}
|
|
46
46
|
</div>
|
|
47
47
|
);
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
const pickGreenByExperience = (experience: number): string => {
|
|
51
|
+
const exp = Math.max(0, Math.min(100, Number(experience || 0)));
|
|
52
|
+
if (exp >= 80) return green1;
|
|
53
|
+
if (exp >= 60) return green2;
|
|
54
|
+
if (exp >= 40) return green3;
|
|
55
|
+
if (exp >= 20) return green4;
|
|
56
|
+
return green5;
|
|
57
|
+
};
|
|
58
|
+
|
|
50
59
|
export default function SkillsBubble({ skillsCategoryRadar, headless }: { skillsCategoryRadar?: SkillsRadarPoint[]; headless?: boolean }) {
|
|
51
60
|
const hasRadar = !!(skillsCategoryRadar && skillsCategoryRadar.length > 0);
|
|
52
61
|
const skillsRadarLimited = (skillsCategoryRadar || []).slice(0, 24);
|
|
@@ -78,9 +87,7 @@ export default function SkillsBubble({ skillsCategoryRadar, headless }: { skills
|
|
|
78
87
|
const value = seriesAvg(d);
|
|
79
88
|
const experience = Math.max(0, Math.min(100, Number(d.experience || 0)));
|
|
80
89
|
const size = Math.max(2, Math.round((value / maxValue) * 100)); // 2..100
|
|
81
|
-
|
|
82
|
-
const saturation = Math.max(20, Math.min(100, experience));
|
|
83
|
-
const color = `hsl(210 ${saturation}% 45%)`;
|
|
90
|
+
const color = 'var(--content-card-background)';
|
|
84
91
|
return {
|
|
85
92
|
label: d.axis,
|
|
86
93
|
value: size,
|
|
@@ -101,8 +108,7 @@ export default function SkillsBubble({ skillsCategoryRadar, headless }: { skills
|
|
|
101
108
|
}, [bubbles]);
|
|
102
109
|
|
|
103
110
|
const colorLegend = useMemo(() => {
|
|
104
|
-
|
|
105
|
-
return steps.map((s) => `hsl(210 ${s}% 45%)`);
|
|
111
|
+
return [green5, green4, green3, green2, green1];
|
|
106
112
|
}, []);
|
|
107
113
|
|
|
108
114
|
const percentLegend = useMemo(() => {
|
|
@@ -137,17 +143,54 @@ export default function SkillsBubble({ skillsCategoryRadar, headless }: { skills
|
|
|
137
143
|
tooltipFunc={(node, d) => {
|
|
138
144
|
try {
|
|
139
145
|
node.innerHTML = '';
|
|
146
|
+
node.className = 'rounded-md border shadow-sm px-3 py-2 text-xs';
|
|
147
|
+
(node as HTMLElement).style.background = 'var(--content-card-background)';
|
|
148
|
+
(node as HTMLElement).style.borderColor = 'var(--icon-button-secondary)';
|
|
149
|
+
(node as HTMLElement).style.color = 'var(--text-main)';
|
|
150
|
+
(node as HTMLElement).style.pointerEvents = 'none';
|
|
151
|
+
|
|
152
|
+
const header = document.createElement('div');
|
|
153
|
+
header.className = 'mb-1';
|
|
140
154
|
const title = document.createElement('div');
|
|
141
|
-
title.
|
|
155
|
+
title.className = 'font-medium';
|
|
156
|
+
title.style.color = 'var(--text-main)';
|
|
142
157
|
title.textContent = String(d.displayText || d._id || '');
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
158
|
+
header.appendChild(title);
|
|
159
|
+
|
|
160
|
+
const content = document.createElement('div');
|
|
161
|
+
content.className = 'space-y-1';
|
|
146
162
|
const ratio = Number(d.value || 0);
|
|
147
163
|
const experience = Number(d.colorValue || 0);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
164
|
+
|
|
165
|
+
const row1 = document.createElement('div');
|
|
166
|
+
row1.className = 'flex items-center justify-between gap-3';
|
|
167
|
+
const row1Left = document.createElement('span');
|
|
168
|
+
row1Left.style.color = 'var(--text-secondary)';
|
|
169
|
+
row1Left.textContent = 'Ratio';
|
|
170
|
+
const row1Right = document.createElement('span');
|
|
171
|
+
row1Right.className = 'font-medium';
|
|
172
|
+
row1Right.style.color = 'var(--text-main)';
|
|
173
|
+
row1Right.textContent = `${ratio}%`;
|
|
174
|
+
row1.appendChild(row1Left);
|
|
175
|
+
row1.appendChild(row1Right);
|
|
176
|
+
|
|
177
|
+
const row2 = document.createElement('div');
|
|
178
|
+
row2.className = 'flex items-center justify-between gap-3';
|
|
179
|
+
const row2Left = document.createElement('span');
|
|
180
|
+
row2Left.style.color = 'var(--text-secondary)';
|
|
181
|
+
row2Left.textContent = 'Experience';
|
|
182
|
+
const row2Right = document.createElement('span');
|
|
183
|
+
row2Right.className = 'font-medium';
|
|
184
|
+
row2Right.style.color = 'var(--text-main)';
|
|
185
|
+
row2Right.textContent = String(experience);
|
|
186
|
+
row2.appendChild(row2Left);
|
|
187
|
+
row2.appendChild(row2Right);
|
|
188
|
+
|
|
189
|
+
content.appendChild(row1);
|
|
190
|
+
content.appendChild(row2);
|
|
191
|
+
|
|
192
|
+
node.appendChild(header);
|
|
193
|
+
node.appendChild(content);
|
|
151
194
|
} catch {}
|
|
152
195
|
}}
|
|
153
196
|
/>
|
|
@@ -183,7 +226,7 @@ export default function SkillsBubble({ skillsCategoryRadar, headless }: { skills
|
|
|
183
226
|
}}
|
|
184
227
|
onMouseLeave={() => setLegendTooltip(null)}
|
|
185
228
|
>
|
|
186
|
-
<span className={'inline-block h-2 w-2 rounded-full'} style={{ backgroundColor:
|
|
229
|
+
<span className={'inline-block h-2 w-2 rounded-full'} style={{ backgroundColor: pickGreenByExperience(item.experience), flexShrink: 0 }} />
|
|
187
230
|
<span className="truncate">{item.label}</span>
|
|
188
231
|
<span className="ml-auto opacity-80">{item.percent}%</span>
|
|
189
232
|
</button>
|
|
@@ -193,12 +236,12 @@ export default function SkillsBubble({ skillsCategoryRadar, headless }: { skills
|
|
|
193
236
|
{/* Legends */}
|
|
194
237
|
<div className="mt-3 grid grid-cols-1 sm:grid-cols-2 gap-2 text-xs" style={{ color: 'var(--text-secondary)' }}>
|
|
195
238
|
<div className="flex items-center gap-2">
|
|
196
|
-
<span className="inline-block h-3 w-3 rounded-full" style={{ background:
|
|
239
|
+
<span className="inline-block h-3 w-3 rounded-full" style={{ background: green1 }} />
|
|
197
240
|
<span>Bubble size: relative ratio of observed/self-reported/certified</span>
|
|
198
241
|
</div>
|
|
199
242
|
<div className="flex items-center gap-2">
|
|
200
|
-
<span className="inline-block h-3 w-3 rounded-full" style={{ background:
|
|
201
|
-
<span>Color
|
|
243
|
+
<span className="inline-block h-3 w-3 rounded-full" style={{ background: green5 }} />
|
|
244
|
+
<span>Color shade: experience (darker = more experienced)</span>
|
|
202
245
|
</div>
|
|
203
246
|
</div>
|
|
204
247
|
</div>
|