Skip to content

Commit c9513ed

Browse files
better dot selector control
1 parent ec48b0e commit c9513ed

File tree

2 files changed

+118
-48
lines changed

2 files changed

+118
-48
lines changed

src/components/OverviewChart.css

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@
2121
flex-wrap: wrap;
2222
}
2323

24-
.overview-chart .chart-controls {
25-
display: flex;
26-
align-items: center;
27-
gap: 0.5rem;
28-
margin-left: auto;
29-
}
30-
3124
.overview-chart .range-button {
3225
padding: 0.35rem 0.75rem;
3326
border: 1px solid var(--border-color);
@@ -57,29 +50,75 @@
5750
box-shadow: 0 6px 14px rgba(102, 126, 234, 0.25);
5851
}
5952

60-
.chart-controls label {
61-
font-size: 0.875rem;
62-
color: var(--text-secondary);
53+
.dot-size-selector {
54+
position: relative;
6355
}
6456

65-
.chart-controls select {
66-
padding: 0.375rem 0.75rem;
57+
.dot-size-button {
58+
display: flex;
59+
align-items: center;
60+
justify-content: center;
61+
width: 36px;
62+
height: 36px;
63+
padding: 0;
6764
border: 1px solid var(--border-color);
68-
border-radius: 0.375rem;
69-
font-size: 0.875rem;
65+
border-radius: 0.5rem;
7066
background: var(--bg-secondary);
71-
color: var(--text-primary);
67+
color: var(--text-secondary);
7268
cursor: pointer;
69+
transition: all 0.2s ease;
7370
}
7471

75-
.chart-controls select:hover {
76-
border-color: var(--border-light);
72+
.dot-size-button:hover {
73+
border-color: var(--primary-color);
74+
color: var(--primary-color);
75+
background: var(--hover-bg);
7776
}
7877

79-
.chart-controls select:focus {
78+
.dot-size-button:focus-visible {
8079
outline: none;
8180
border-color: var(--primary-color);
82-
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
81+
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15);
82+
}
83+
84+
.dot-size-menu {
85+
position: absolute;
86+
top: calc(100% + 4px);
87+
right: 0;
88+
min-width: 180px;
89+
background: var(--bg-secondary);
90+
border: 1px solid var(--border-color);
91+
border-radius: 0.5rem;
92+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
93+
overflow: hidden;
94+
z-index: 100;
95+
}
96+
97+
.dot-size-menu-item {
98+
display: block;
99+
width: 100%;
100+
padding: 0.65rem 0.875rem;
101+
border: none;
102+
background: transparent;
103+
color: var(--text-primary);
104+
font-size: 0.875rem;
105+
text-align: left;
106+
cursor: pointer;
107+
transition: background 0.15s ease;
108+
}
109+
110+
.dot-size-menu-item:hover {
111+
background: var(--hover-bg);
112+
}
113+
114+
.dot-size-menu-item.active {
115+
background: rgba(102, 126, 234, 0.1);
116+
color: var(--primary-color);
117+
font-weight: 600;
118+
}
119+
120+
.dot-size-menu-item:not(:last-child) {
121+
border-bottom: 1px solid var(--border-light);
83122
}
84123

85124
.legend {
@@ -294,19 +333,17 @@
294333
}
295334

296335
.overview-chart .chart-header {
297-
flex-direction: column;
298-
align-items: flex-start;
299-
gap: 0.75rem;
336+
flex-direction: row;
337+
align-items: center;
338+
gap: 0.5rem;
300339
}
301340

302341
.overview-chart .chart-range-controls {
303-
width: 100%;
342+
flex: 1;
304343
}
305344

306-
.overview-chart .chart-controls {
307-
flex-wrap: wrap;
308-
justify-content: flex-start;
309-
margin-left: 0;
345+
.dot-size-selector {
346+
flex-shrink: 0;
310347
}
311348

312349
.mobile-model-list {
@@ -338,14 +375,6 @@
338375
padding: 0.75rem 0.5rem;
339376
}
340377

341-
.chart-controls label {
342-
font-size: 0.8rem;
343-
}
344-
345-
.chart-controls select {
346-
font-size: 0.8rem;
347-
}
348-
349378
.legend {
350379
gap: 0.75rem;
351380
}

src/components/OverviewChart.jsx

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ export default function OverviewChart({
3131
const [mobileModelSummaries, setMobileModelSummaries] = useState([]);
3232
const [hoveredModel, setHoveredModel] = useState(null);
3333
const [activeModel, setActiveModel] = useState(null);
34+
const [dotSizeMenuOpen, setDotSizeMenuOpen] = useState(false);
3435
const hoveredModelRef = useRef(null);
36+
const dotSizeButtonRef = useRef(null);
3537

3638
const parseDateUtc = (dateStr) => {
3739
const [year, month, day] = dateStr.split('-').map(Number);
@@ -220,6 +222,17 @@ export default function OverviewChart({
220222
return () => window.removeEventListener('resize', handleResize);
221223
}, []);
222224

225+
useEffect(() => {
226+
if (!dotSizeMenuOpen) return;
227+
const handleClickOutside = (event) => {
228+
if (dotSizeButtonRef.current && !dotSizeButtonRef.current.contains(event.target)) {
229+
setDotSizeMenuOpen(false);
230+
}
231+
};
232+
document.addEventListener('mousedown', handleClickOutside);
233+
return () => document.removeEventListener('mousedown', handleClickOutside);
234+
}, [dotSizeMenuOpen]);
235+
223236
useEffect(() => {
224237
const highlightModel = hoveredModel || activeModel;
225238
hoveredModelRef.current = highlightModel;
@@ -521,7 +534,7 @@ export default function OverviewChart({
521534

522535
const withAverage = modelSummaries
523536
.filter(summary => summary.average !== null && summary.average !== undefined)
524-
.sort((a, b) => a.average - b.average);
537+
.sort((a, b) => b.average - a.average);
525538
const withoutAverage = modelSummaries.filter(summary => summary.average === null || summary.average === undefined);
526539
const sortedSummaries = [...withAverage, ...withoutAverage];
527540

@@ -617,16 +630,16 @@ export default function OverviewChart({
617630
},
618631
layout: {
619632
padding: {
620-
right: showModelLabels ? 140 : 24 // Reduce padding when labels hidden
633+
right: showModelLabels ? 140 : 24, // Reduce padding when labels hidden
634+
bottom: 20 // Add bottom padding for date labels
621635
}
622636
},
623637
plugins: {
624638
legend: {
625639
display: false
626640
},
627641
title: {
628-
display: true,
629-
text: 'Average Price Trends Across All Sources'
642+
display: false
630643
},
631644
tooltip: {
632645
displayColors: false, // Remove the colored box
@@ -1153,16 +1166,44 @@ export default function OverviewChart({
11531166
);
11541167
})}
11551168
</div>
1156-
<div className="chart-controls">
1157-
<label htmlFor="dot-size-mode">Show:</label>
1158-
<select
1159-
id="dot-size-mode"
1160-
value={dotSizeMode}
1161-
onChange={(e) => setDotSizeMode(e.target.value)}
1169+
<div className="dot-size-selector" ref={dotSizeButtonRef}>
1170+
<button
1171+
type="button"
1172+
className="dot-size-button"
1173+
onClick={() => setDotSizeMenuOpen(!dotSizeMenuOpen)}
1174+
aria-label="Change dot size mode"
1175+
aria-expanded={dotSizeMenuOpen}
11621176
>
1163-
<option value="stock">Stock Count</option>
1164-
<option value="days">Avg Days on Market</option>
1165-
</select>
1177+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1178+
<circle cx="12" cy="12" r="4.5" fill="currentColor" opacity="0.3" />
1179+
<path d="M3 12 L7.5 12 M16.5 12 L21 12" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
1180+
<path d="M4.5 10 L3 12 L4.5 14 M19.5 10 L21 12 L19.5 14" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" fill="none" />
1181+
</svg>
1182+
</button>
1183+
{dotSizeMenuOpen && (
1184+
<div className="dot-size-menu">
1185+
<button
1186+
type="button"
1187+
className={`dot-size-menu-item${dotSizeMode === 'stock' ? ' active' : ''}`}
1188+
onClick={() => {
1189+
setDotSizeMode('stock');
1190+
setDotSizeMenuOpen(false);
1191+
}}
1192+
>
1193+
Stock Count
1194+
</button>
1195+
<button
1196+
type="button"
1197+
className={`dot-size-menu-item${dotSizeMode === 'days' ? ' active' : ''}`}
1198+
onClick={() => {
1199+
setDotSizeMode('days');
1200+
setDotSizeMenuOpen(false);
1201+
}}
1202+
>
1203+
Avg Days on Market
1204+
</button>
1205+
</div>
1206+
)}
11661207
</div>
11671208
</div>
11681209
{!showModelLabels && mobileModelSummaries.length > 0 && (

0 commit comments

Comments
 (0)