@stfrigerio/sito-template 0.1.21 → 0.1.23
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/dist/components/organisms/Table/Table.d.ts +115 -0
- package/dist/components/organisms/Table/Table.d.ts.map +1 -0
- package/dist/components/organisms/Table/Table.stories.d.ts +43 -0
- package/dist/components/organisms/Table/Table.stories.d.ts.map +1 -0
- package/dist/components/organisms/Table/index.d.ts +3 -0
- package/dist/components/organisms/Table/index.d.ts.map +1 -0
- package/dist/components/organisms/charts/QuantifiableHabitsChart/QuantifiableHabitsChart.d.ts +1 -0
- package/dist/components/organisms/charts/QuantifiableHabitsChart/QuantifiableHabitsChart.d.ts.map +1 -1
- package/dist/components/organisms/charts/SleepChart/SleepChart.d.ts.map +1 -1
- package/dist/components/organisms/charts/SunburstChart/SunburstChart.d.ts.map +1 -1
- package/dist/index.esm.js +291 -102
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +291 -102
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2256,7 +2256,7 @@ const MoodChart = ({ moodData, width = 800, height = 400 }) => {
|
|
|
2256
2256
|
}, children: [jsxRuntime.jsxs("div", { className: styles$6.tooltipHeader, children: [jsxRuntime.jsx("div", { className: styles$6.tooltipDate, children: selectedMood.date.toLocaleDateString() }), jsxRuntime.jsxs("div", { className: styles$6.tooltipRating, children: [jsxRuntime.jsx("span", { className: styles$6.ratingValue, children: selectedMood.rating }), jsxRuntime.jsx("span", { className: styles$6.ratingMax, children: "/10" })] })] }), selectedMood.tags.length > 0 && (jsxRuntime.jsx("div", { className: styles$6.tooltipTags, children: selectedMood.tags.map((tag, index) => (jsxRuntime.jsx("span", { className: styles$6.tag, children: tag }, index))) })), selectedMood.comment && (jsxRuntime.jsx("div", { className: styles$6.tooltipComment, children: selectedMood.comment }))] }))] }));
|
|
2257
2257
|
};
|
|
2258
2258
|
|
|
2259
|
-
var styles$5 = {"container":"QuantifiableHabitsChart-module_container__X5SBp","controls":"QuantifiableHabitsChart-module_controls__O-ObQ","viewToggle":"QuantifiableHabitsChart-module_viewToggle__24hKA","viewButton":"QuantifiableHabitsChart-module_viewButton__WFU6j","active":"QuantifiableHabitsChart-module_active__Pjqy9","viewIcon":"QuantifiableHabitsChart-module_viewIcon__b2mfk","viewLabel":"QuantifiableHabitsChart-module_viewLabel__9MjCU","legend":"QuantifiableHabitsChart-module_legend__3Ki7c","legendItem":"QuantifiableHabitsChart-module_legendItem__Zl7fz","inactive":"QuantifiableHabitsChart-module_inactive__TzZC-","legendColor":"QuantifiableHabitsChart-module_legendColor__zbPoV","legendEmoji":"QuantifiableHabitsChart-module_legendEmoji__HG9CZ","legendLabel":"QuantifiableHabitsChart-module_legendLabel__H3oFL","chart":"QuantifiableHabitsChart-module_chart__FMeA-","gridLine":"QuantifiableHabitsChart-module_gridLine__CTNIq","line":"QuantifiableHabitsChart-module_line__CpYip","xAxis":"QuantifiableHabitsChart-module_xAxis__lbgBG","yAxis":"QuantifiableHabitsChart-module_yAxis__Y6WeV","dataPoint":"QuantifiableHabitsChart-module_dataPoint__s8UMX"};
|
|
2259
|
+
var styles$5 = {"container":"QuantifiableHabitsChart-module_container__X5SBp","controls":"QuantifiableHabitsChart-module_controls__O-ObQ","viewToggle":"QuantifiableHabitsChart-module_viewToggle__24hKA","viewButton":"QuantifiableHabitsChart-module_viewButton__WFU6j","active":"QuantifiableHabitsChart-module_active__Pjqy9","viewIcon":"QuantifiableHabitsChart-module_viewIcon__b2mfk","viewLabel":"QuantifiableHabitsChart-module_viewLabel__9MjCU","legend":"QuantifiableHabitsChart-module_legend__3Ki7c","legendItem":"QuantifiableHabitsChart-module_legendItem__Zl7fz","inactive":"QuantifiableHabitsChart-module_inactive__TzZC-","legendColor":"QuantifiableHabitsChart-module_legendColor__zbPoV","legendEmoji":"QuantifiableHabitsChart-module_legendEmoji__HG9CZ","legendLabel":"QuantifiableHabitsChart-module_legendLabel__H3oFL","chart":"QuantifiableHabitsChart-module_chart__FMeA-","gridLine":"QuantifiableHabitsChart-module_gridLine__CTNIq","line":"QuantifiableHabitsChart-module_line__CpYip","xAxis":"QuantifiableHabitsChart-module_xAxis__lbgBG","yAxis":"QuantifiableHabitsChart-module_yAxis__Y6WeV","dataPoint":"QuantifiableHabitsChart-module_dataPoint__s8UMX","tooltip":"QuantifiableHabitsChart-module_tooltip__Fay8N","visible":"QuantifiableHabitsChart-module_visible__-KSJq","tooltipHeader":"QuantifiableHabitsChart-module_tooltipHeader__7Q2up","tooltipEmoji":"QuantifiableHabitsChart-module_tooltipEmoji__atV3T","tooltipDot":"QuantifiableHabitsChart-module_tooltipDot__YbdFh","tooltipInfo":"QuantifiableHabitsChart-module_tooltipInfo__XC7WF","tooltipDate":"QuantifiableHabitsChart-module_tooltipDate__6V6Xi","tooltipValue":"QuantifiableHabitsChart-module_tooltipValue__ldASB"};
|
|
2260
2260
|
|
|
2261
2261
|
// Default colors as fallback
|
|
2262
2262
|
const DEFAULT_HABIT_COLORS = {
|
|
@@ -2269,11 +2269,13 @@ const DEFAULT_HABIT_COLORS = {
|
|
|
2269
2269
|
'Calories': '#FF9F1C',
|
|
2270
2270
|
'Study': '#C774E8'
|
|
2271
2271
|
};
|
|
2272
|
-
const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewType = 'daily', periodType = 'month', habitColors: customHabitColors = {}, habitEmojis: customHabitEmojis = {} }) => {
|
|
2272
|
+
const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewType = 'daily', periodType = 'month', habitColors: customHabitColors = {}, habitEmojis: customHabitEmojis = {}, onDataPointClick }) => {
|
|
2273
2273
|
const svgRef = React.useRef(null);
|
|
2274
|
+
const tooltipRef = React.useRef(null);
|
|
2274
2275
|
const [viewType, setViewType] = React.useState(defaultViewType);
|
|
2275
2276
|
const [activeHabits, setActiveHabits] = React.useState([]);
|
|
2276
2277
|
const [hoveredHabit, setHoveredHabit] = React.useState(null);
|
|
2278
|
+
const [tooltipData, setTooltipData] = React.useState(null);
|
|
2277
2279
|
const margin = { top: 20, right: 20, bottom: 50, left: 50 };
|
|
2278
2280
|
const chartWidth = width - margin.left - margin.right;
|
|
2279
2281
|
const chartHeight = height - margin.top - margin.bottom;
|
|
@@ -2281,6 +2283,16 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
2281
2283
|
React.useEffect(() => {
|
|
2282
2284
|
setActiveHabits(habits);
|
|
2283
2285
|
}, [habits]);
|
|
2286
|
+
// Hide tooltip on scroll
|
|
2287
|
+
React.useEffect(() => {
|
|
2288
|
+
const handleScroll = () => {
|
|
2289
|
+
setTooltipData(null);
|
|
2290
|
+
};
|
|
2291
|
+
window.addEventListener('scroll', handleScroll, true);
|
|
2292
|
+
return () => {
|
|
2293
|
+
window.removeEventListener('scroll', handleScroll, true);
|
|
2294
|
+
};
|
|
2295
|
+
}, []);
|
|
2284
2296
|
const availableViewTypes = React.useMemo(() => {
|
|
2285
2297
|
switch (periodType) {
|
|
2286
2298
|
case 'week':
|
|
@@ -2369,6 +2381,15 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
2369
2381
|
return;
|
|
2370
2382
|
const svg = d3__namespace.select(svgRef.current);
|
|
2371
2383
|
svg.selectAll('*').remove();
|
|
2384
|
+
// Add invisible rect to detect mouse leave events
|
|
2385
|
+
svg.append('rect')
|
|
2386
|
+
.attr('width', width)
|
|
2387
|
+
.attr('height', height)
|
|
2388
|
+
.attr('fill', 'transparent')
|
|
2389
|
+
.style('pointer-events', 'all')
|
|
2390
|
+
.on('mouseleave', () => {
|
|
2391
|
+
setTooltipData(null);
|
|
2392
|
+
});
|
|
2372
2393
|
const g = svg.append('g')
|
|
2373
2394
|
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
2374
2395
|
const dates = aggregateData.dates.map(d => new Date(d));
|
|
@@ -2399,15 +2420,56 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
2399
2420
|
.attr('d', line)
|
|
2400
2421
|
.attr('stroke', getColor(habit))
|
|
2401
2422
|
.attr('opacity', hoveredHabit && hoveredHabit !== habit ? 0.3 : 1);
|
|
2423
|
+
// Add invisible larger circles for better hover detection
|
|
2424
|
+
g.selectAll(`.hover-circle-${habit}`)
|
|
2425
|
+
.data(habitData)
|
|
2426
|
+
.enter().append('circle')
|
|
2427
|
+
.attr('cx', d => xScale(d[0]))
|
|
2428
|
+
.attr('cy', d => yScale(d[1]))
|
|
2429
|
+
.attr('r', 10) // Larger invisible area
|
|
2430
|
+
.attr('fill', 'transparent')
|
|
2431
|
+
.style('cursor', 'pointer')
|
|
2432
|
+
.on('mouseenter', function (_event, d) {
|
|
2433
|
+
const [date, value] = d;
|
|
2434
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
2435
|
+
if (rect) {
|
|
2436
|
+
setTooltipData({
|
|
2437
|
+
habit,
|
|
2438
|
+
date: date.toISOString().split('T')[0],
|
|
2439
|
+
value,
|
|
2440
|
+
x: xScale(date) + margin.left + rect.left,
|
|
2441
|
+
y: yScale(value) + margin.top + rect.top
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
// Find and enlarge the visible circle
|
|
2445
|
+
d3__namespace.select(g.node())
|
|
2446
|
+
.selectAll(`.circle-${habit}-${dates.indexOf(date)}`)
|
|
2447
|
+
.attr('r', 6);
|
|
2448
|
+
})
|
|
2449
|
+
.on('mouseleave', function (_event, _d) {
|
|
2450
|
+
setTooltipData(null);
|
|
2451
|
+
// Reset all circles for this habit
|
|
2452
|
+
d3__namespace.select(g.node())
|
|
2453
|
+
.selectAll('[class*="circle-' + habit + '"]')
|
|
2454
|
+
.attr('r', 4);
|
|
2455
|
+
})
|
|
2456
|
+
.on('click', function (_event, d) {
|
|
2457
|
+
const [date, value] = d;
|
|
2458
|
+
if (onDataPointClick) {
|
|
2459
|
+
onDataPointClick(habit, date.toISOString().split('T')[0], value);
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
// Add visible circles
|
|
2402
2463
|
g.selectAll(`.circle-${habit}`)
|
|
2403
2464
|
.data(habitData)
|
|
2404
2465
|
.enter().append('circle')
|
|
2405
|
-
.attr('class', styles$5.dataPoint)
|
|
2466
|
+
.attr('class', (_d, i) => `${styles$5.dataPoint} circle-${habit}-${i}`)
|
|
2406
2467
|
.attr('cx', d => xScale(d[0]))
|
|
2407
2468
|
.attr('cy', d => yScale(d[1]))
|
|
2408
2469
|
.attr('r', 4)
|
|
2409
2470
|
.attr('fill', getColor(habit))
|
|
2410
|
-
.attr('opacity', hoveredHabit && hoveredHabit !== habit ? 0.3 : 1)
|
|
2471
|
+
.attr('opacity', hoveredHabit && hoveredHabit !== habit ? 0.3 : 1)
|
|
2472
|
+
.style('pointer-events', 'none'); // Let the invisible circle handle events
|
|
2411
2473
|
});
|
|
2412
2474
|
// Helper function to get ISO week number
|
|
2413
2475
|
const getISOWeek = (date) => {
|
|
@@ -2477,7 +2539,28 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
2477
2539
|
g.append('g')
|
|
2478
2540
|
.attr('class', styles$5.yAxis)
|
|
2479
2541
|
.call(d3__namespace.axisLeft(yScale));
|
|
2480
|
-
}, [aggregateData, activeHabits, chartWidth, chartHeight, margin, hoveredHabit, customHabitColors]);
|
|
2542
|
+
}, [aggregateData, activeHabits, chartWidth, chartHeight, margin, hoveredHabit, customHabitColors, onDataPointClick]);
|
|
2543
|
+
// Format date for tooltip display
|
|
2544
|
+
const formatTooltipDate = (dateStr, viewType) => {
|
|
2545
|
+
const date = new Date(dateStr);
|
|
2546
|
+
switch (viewType) {
|
|
2547
|
+
case 'daily':
|
|
2548
|
+
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
2549
|
+
case 'weekly': {
|
|
2550
|
+
const weekEnd = new Date(date);
|
|
2551
|
+
weekEnd.setDate(date.getDate() + 6);
|
|
2552
|
+
return `Week of ${date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`;
|
|
2553
|
+
}
|
|
2554
|
+
case 'monthly':
|
|
2555
|
+
return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
|
2556
|
+
case 'quarterly': {
|
|
2557
|
+
const quarter = Math.floor(date.getMonth() / 3) + 1;
|
|
2558
|
+
return `Q${quarter} ${date.getFullYear()}`;
|
|
2559
|
+
}
|
|
2560
|
+
default:
|
|
2561
|
+
return dateStr;
|
|
2562
|
+
}
|
|
2563
|
+
};
|
|
2481
2564
|
// View type icons and labels
|
|
2482
2565
|
const viewTypeConfig = {
|
|
2483
2566
|
daily: { icon: '📅', label: 'Daily' },
|
|
@@ -2485,42 +2568,57 @@ const QuantifiableHabitsChart = ({ data, width = 800, height = 400, defaultViewT
|
|
|
2485
2568
|
monthly: { icon: '🗓️', label: 'Monthly' },
|
|
2486
2569
|
quarterly: { icon: '📊', label: 'Quarterly' }
|
|
2487
2570
|
};
|
|
2488
|
-
return (jsxRuntime.jsxs("div", { className: styles$5.container, children: [jsxRuntime.jsx("div", { className: styles$5.controls, children: jsxRuntime.jsx("div", { className: styles$5.viewToggle, children: availableViewTypes.map(type => (jsxRuntime.jsxs("button", { className: `${styles$5.viewButton} ${viewType === type ? styles$5.active : ''}`, onClick: () => setViewType(type), title: viewTypeConfig[type].label, children: [jsxRuntime.jsx("span", { className: styles$5.viewIcon, children: viewTypeConfig[type].icon }), jsxRuntime.jsx("span", { className: styles$5.viewLabel, children: viewTypeConfig[type].label })] }, type))) }) }), jsxRuntime.jsx("div", { className: styles$5.legend, children: habits.map(habit => (jsxRuntime.jsxs("button", { className: `${styles$5.legendItem} ${!activeHabits.includes(habit) ? styles$5.inactive : ''}`, onClick: () => toggleHabit(habit), onMouseEnter: () => setHoveredHabit(habit), onMouseLeave: () => setHoveredHabit(null), children: [jsxRuntime.jsx("span", { className: styles$5.legendEmoji, children: customHabitEmojis[habit] || '📊' }), jsxRuntime.jsx("span", { className: styles$5.legendColor, style: { backgroundColor: getColor(habit) } }), jsxRuntime.jsx("span", { className: styles$5.legendLabel, children: habit })] }, habit))) }), jsxRuntime.jsx("svg", { ref: svgRef, width: width, height: height, className: styles$5.chart })
|
|
2571
|
+
return (jsxRuntime.jsxs("div", { className: styles$5.container, children: [jsxRuntime.jsx("div", { className: styles$5.controls, children: jsxRuntime.jsx("div", { className: styles$5.viewToggle, children: availableViewTypes.map(type => (jsxRuntime.jsxs("button", { className: `${styles$5.viewButton} ${viewType === type ? styles$5.active : ''}`, onClick: () => setViewType(type), title: viewTypeConfig[type].label, children: [jsxRuntime.jsx("span", { className: styles$5.viewIcon, children: viewTypeConfig[type].icon }), jsxRuntime.jsx("span", { className: styles$5.viewLabel, children: viewTypeConfig[type].label })] }, type))) }) }), jsxRuntime.jsx("div", { className: styles$5.legend, children: habits.map(habit => (jsxRuntime.jsxs("button", { className: `${styles$5.legendItem} ${!activeHabits.includes(habit) ? styles$5.inactive : ''}`, onClick: () => toggleHabit(habit), onMouseEnter: () => setHoveredHabit(habit), onMouseLeave: () => setHoveredHabit(null), children: [jsxRuntime.jsx("span", { className: styles$5.legendEmoji, children: customHabitEmojis[habit] || '📊' }), jsxRuntime.jsx("span", { className: styles$5.legendColor, style: { backgroundColor: getColor(habit) } }), jsxRuntime.jsx("span", { className: styles$5.legendLabel, children: habit })] }, habit))) }), jsxRuntime.jsx("svg", { ref: svgRef, width: width, height: height, className: styles$5.chart }), tooltipData && (jsxRuntime.jsxs("div", { ref: tooltipRef, className: `${styles$5.tooltip} ${tooltipData ? styles$5.visible : ''}`, style: {
|
|
2572
|
+
left: `${tooltipData.x}px`,
|
|
2573
|
+
top: `${tooltipData.y - 80}px`
|
|
2574
|
+
}, children: [jsxRuntime.jsxs("div", { className: styles$5.tooltipHeader, children: [jsxRuntime.jsx("span", { className: styles$5.tooltipEmoji, children: customHabitEmojis[tooltipData.habit] || '📊' }), jsxRuntime.jsx("span", { className: styles$5.tooltipDot, style: { backgroundColor: getColor(tooltipData.habit) } }), jsxRuntime.jsx("span", { children: tooltipData.habit })] }), jsxRuntime.jsxs("div", { className: styles$5.tooltipInfo, children: [jsxRuntime.jsx("div", { className: styles$5.tooltipDate, children: formatTooltipDate(tooltipData.date, viewType) }), jsxRuntime.jsxs("div", { className: styles$5.tooltipValue, children: [jsxRuntime.jsx("strong", { children: Math.round(tooltipData.value * 10) / 10 }), jsxRuntime.jsx("span", { style: { fontSize: '12px', fontWeight: 'normal', opacity: 0.6 }, children: "units" })] })] })] }))] }));
|
|
2489
2575
|
};
|
|
2490
2576
|
|
|
2491
|
-
var styles$4 = {"container":"SleepChart-module_container__RjHVU","header":"SleepChart-module_header__bcoWe","title":"SleepChart-module_title__6-QII","legend":"SleepChart-module_legend__VsqQj","legendItem":"SleepChart-module_legendItem__2c1nd","sleepDot":"SleepChart-module_sleepDot__qCY6O","wakeDot":"SleepChart-module_wakeDot__-RcrT","chart":"SleepChart-module_chart__jC1nL","gridLine":"SleepChart-module_gridLine__hi715","sleepBar":"SleepChart-module_sleepBar__Hk76f","xAxis":"SleepChart-module_xAxis__xzXyM","yAxis":"SleepChart-module_yAxis__7N-LA"};
|
|
2577
|
+
var styles$4 = {"container":"SleepChart-module_container__RjHVU","header":"SleepChart-module_header__bcoWe","title":"SleepChart-module_title__6-QII","legend":"SleepChart-module_legend__VsqQj","legendItem":"SleepChart-module_legendItem__2c1nd","sleepDot":"SleepChart-module_sleepDot__qCY6O","wakeDot":"SleepChart-module_wakeDot__-RcrT","chart":"SleepChart-module_chart__jC1nL","gridLine":"SleepChart-module_gridLine__hi715","sleepBar":"SleepChart-module_sleepBar__Hk76f","xAxis":"SleepChart-module_xAxis__xzXyM","yAxis":"SleepChart-module_yAxis__7N-LA","tooltip":"SleepChart-module_tooltip__jQBv1","visible":"SleepChart-module_visible__wy0ck","tooltipHeader":"SleepChart-module_tooltipHeader__5BdPL","tooltipEmoji":"SleepChart-module_tooltipEmoji__c5vtz","tooltipInfo":"SleepChart-module_tooltipInfo__9Yrno","tooltipRow":"SleepChart-module_tooltipRow__CuDaE","tooltipLabel":"SleepChart-module_tooltipLabel__7SNzQ","tooltipValue":"SleepChart-module_tooltipValue__FoAVy","tooltipDuration":"SleepChart-module_tooltipDuration__d2wBW"};
|
|
2492
2578
|
|
|
2493
2579
|
const parseTimeToDecimal = (time) => {
|
|
2494
2580
|
const [hours, minutes] = time.split(':').map(Number);
|
|
2495
2581
|
return hours + minutes / 60;
|
|
2496
2582
|
};
|
|
2497
|
-
const
|
|
2583
|
+
const formatTime = (hour) => {
|
|
2584
|
+
const h24 = Math.floor(hour % 24);
|
|
2585
|
+
const h12 = h24 === 0 ? 12 : h24 > 12 ? h24 - 12 : h24;
|
|
2586
|
+
const ampm = h24 >= 12 ? 'PM' : 'AM';
|
|
2587
|
+
const minutes = Math.round((hour % 1) * 60);
|
|
2588
|
+
return `${Math.floor(h12)}:${minutes.toString().padStart(2, '0')} ${ampm}`;
|
|
2589
|
+
};
|
|
2590
|
+
const SleepChart = ({ sleepData, width = 800, height = 400, onDateClick }) => {
|
|
2498
2591
|
const svgRef = React.useRef(null);
|
|
2499
|
-
const
|
|
2592
|
+
const [tooltipData, setTooltipData] = React.useState(null);
|
|
2593
|
+
const margin = React.useMemo(() => ({ top: 30, right: 30, bottom: 50, left: 80 }), []);
|
|
2500
2594
|
const chartWidth = width - margin.left - margin.right;
|
|
2501
2595
|
const chartHeight = height - margin.top - margin.bottom;
|
|
2596
|
+
// Hide tooltip on scroll
|
|
2597
|
+
React.useEffect(() => {
|
|
2598
|
+
const handleScroll = () => {
|
|
2599
|
+
setTooltipData(null);
|
|
2600
|
+
};
|
|
2601
|
+
window.addEventListener('scroll', handleScroll, true);
|
|
2602
|
+
return () => {
|
|
2603
|
+
window.removeEventListener('scroll', handleScroll, true);
|
|
2604
|
+
};
|
|
2605
|
+
}, []);
|
|
2502
2606
|
React.useEffect(() => {
|
|
2503
2607
|
if (!svgRef.current || sleepData.length === 0)
|
|
2504
2608
|
return;
|
|
2505
2609
|
const svg = d3__namespace.select(svgRef.current);
|
|
2506
2610
|
svg.selectAll('*').remove();
|
|
2611
|
+
// Add invisible rect to detect mouse leave events
|
|
2612
|
+
svg.append('rect')
|
|
2613
|
+
.attr('width', width)
|
|
2614
|
+
.attr('height', height)
|
|
2615
|
+
.attr('fill', 'transparent')
|
|
2616
|
+
.style('pointer-events', 'all')
|
|
2617
|
+
.on('mouseleave', () => {
|
|
2618
|
+
setTooltipData(null);
|
|
2619
|
+
});
|
|
2507
2620
|
const g = svg.append('g')
|
|
2508
2621
|
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
2509
|
-
// Create tooltip
|
|
2510
|
-
const tooltip = d3__namespace.select('body').append('div')
|
|
2511
|
-
.attr('class', 'sleep-chart-tooltip')
|
|
2512
|
-
.style('position', 'absolute')
|
|
2513
|
-
.style('background', 'var(--bg-secondary)')
|
|
2514
|
-
.style('color', 'var(--text-primary)')
|
|
2515
|
-
.style('padding', '12px')
|
|
2516
|
-
.style('border-radius', '8px')
|
|
2517
|
-
.style('box-shadow', '0 4px 12px rgba(0,0,0,0.25)')
|
|
2518
|
-
.style('border', '1px solid var(--border-primary)')
|
|
2519
|
-
.style('font-size', '14px')
|
|
2520
|
-
.style('pointer-events', 'none')
|
|
2521
|
-
.style('opacity', 0)
|
|
2522
|
-
.style('z-index', '1000')
|
|
2523
|
-
.style('backdrop-filter', 'blur(8px)');
|
|
2524
2622
|
const xScale = d3__namespace.scaleLinear()
|
|
2525
2623
|
.domain([18, 42])
|
|
2526
2624
|
.range([0, chartWidth]);
|
|
@@ -2528,16 +2626,39 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2528
2626
|
const yScale = d3__namespace.scaleBand()
|
|
2529
2627
|
.domain(yDomain)
|
|
2530
2628
|
.range([0, chartHeight])
|
|
2531
|
-
.paddingInner(0.
|
|
2532
|
-
.paddingOuter(0.
|
|
2629
|
+
.paddingInner(0.2)
|
|
2630
|
+
.paddingOuter(0.1);
|
|
2631
|
+
// Add vertical grid lines with midnight/noon highlights
|
|
2533
2632
|
g.selectAll('.grid-line-x')
|
|
2534
|
-
.data(d3__namespace.range(18, 43,
|
|
2633
|
+
.data(d3__namespace.range(18, 43, 1))
|
|
2535
2634
|
.enter().append('line')
|
|
2536
2635
|
.attr('class', styles$4.gridLine)
|
|
2537
2636
|
.attr('x1', d => xScale(d))
|
|
2538
2637
|
.attr('y1', 0)
|
|
2539
2638
|
.attr('x2', d => xScale(d))
|
|
2540
|
-
.attr('y2', chartHeight)
|
|
2639
|
+
.attr('y2', chartHeight)
|
|
2640
|
+
.style('stroke', d => {
|
|
2641
|
+
const hour = d % 24;
|
|
2642
|
+
if (hour === 0)
|
|
2643
|
+
return 'var(--color-border)'; // Midnight
|
|
2644
|
+
if (hour === 12)
|
|
2645
|
+
return 'var(--color-border)'; // Noon
|
|
2646
|
+
return 'var(--color-border)';
|
|
2647
|
+
})
|
|
2648
|
+
.style('stroke-opacity', d => {
|
|
2649
|
+
const hour = d % 24;
|
|
2650
|
+
if (hour === 0 || hour === 12)
|
|
2651
|
+
return 0.3;
|
|
2652
|
+
if (hour % 3 === 0)
|
|
2653
|
+
return 0.15;
|
|
2654
|
+
return 0.05;
|
|
2655
|
+
})
|
|
2656
|
+
.style('stroke-width', d => {
|
|
2657
|
+
const hour = d % 24;
|
|
2658
|
+
if (hour === 0 || hour === 12)
|
|
2659
|
+
return 2;
|
|
2660
|
+
return 1;
|
|
2661
|
+
});
|
|
2541
2662
|
sleepData.forEach((dayData) => {
|
|
2542
2663
|
const yValue = yScale(dayData.date);
|
|
2543
2664
|
if (yValue === undefined)
|
|
@@ -2575,37 +2696,60 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2575
2696
|
}
|
|
2576
2697
|
// Draw the bar only if both values exist
|
|
2577
2698
|
if (sleepHour !== null && wakeHour !== null) {
|
|
2578
|
-
sleepGroup.append('rect')
|
|
2699
|
+
const rect = sleepGroup.append('rect')
|
|
2579
2700
|
.attr('x', xScale(sleepHour))
|
|
2580
2701
|
.attr('y', yValue)
|
|
2581
|
-
.attr('width',
|
|
2702
|
+
.attr('width', 0) // Start with 0 width for animation
|
|
2582
2703
|
.attr('height', barHeight)
|
|
2583
|
-
.attr('rx',
|
|
2704
|
+
.attr('rx', barHeight / 2)
|
|
2584
2705
|
.attr('fill', 'url(#sleepGradient)')
|
|
2585
|
-
.attr('opacity', 0
|
|
2586
|
-
.attr('stroke', '
|
|
2706
|
+
.attr('opacity', 0) // Start invisible
|
|
2707
|
+
.attr('stroke', 'rgba(255,255,255,0.3)')
|
|
2708
|
+
.attr('stroke-width', 1)
|
|
2709
|
+
.style('filter', 'drop-shadow(0 2px 8px rgba(155, 89, 182, 0.3))');
|
|
2710
|
+
// Animate the bar
|
|
2711
|
+
rect.transition()
|
|
2712
|
+
.duration(800)
|
|
2713
|
+
.delay(Math.random() * 200) // Stagger the animations
|
|
2714
|
+
.ease(d3__namespace.easeCubicOut)
|
|
2715
|
+
.attr('width', Math.max(0, xScale(wakeHour) - xScale(sleepHour)))
|
|
2716
|
+
.attr('opacity', 0.85);
|
|
2587
2717
|
}
|
|
2588
2718
|
// Draw sleep dot if sleep time exists
|
|
2589
2719
|
if (sleepHour !== null) {
|
|
2590
|
-
sleepGroup.append('circle')
|
|
2720
|
+
const sleepDot = sleepGroup.append('circle')
|
|
2591
2721
|
.attr('cx', xScale(sleepHour))
|
|
2592
2722
|
.attr('cy', yValue + barHeight / 2)
|
|
2593
|
-
.attr('r',
|
|
2723
|
+
.attr('r', 0) // Start with 0 radius
|
|
2594
2724
|
.attr('fill', '#9B59B6')
|
|
2595
|
-
.attr('stroke', '#
|
|
2596
|
-
.attr('stroke-width',
|
|
2597
|
-
.style('filter', 'drop-shadow(0
|
|
2725
|
+
.attr('stroke', '#ffffff')
|
|
2726
|
+
.attr('stroke-width', 2)
|
|
2727
|
+
.style('filter', 'drop-shadow(0 3px 6px rgba(155, 89, 182, 0.4))')
|
|
2728
|
+
.style('transition', 'all 0.3s ease');
|
|
2729
|
+
// Animate the dot
|
|
2730
|
+
sleepDot.transition()
|
|
2731
|
+
.duration(400)
|
|
2732
|
+
.delay(300 + Math.random() * 100)
|
|
2733
|
+
.ease(d3__namespace.easeBackOut)
|
|
2734
|
+
.attr('r', 6);
|
|
2598
2735
|
}
|
|
2599
2736
|
// Draw wake dot if wake hour exists
|
|
2600
2737
|
if (wakeHour !== null) {
|
|
2601
|
-
sleepGroup.append('circle')
|
|
2738
|
+
const wakeDot = sleepGroup.append('circle')
|
|
2602
2739
|
.attr('cx', xScale(wakeHour))
|
|
2603
2740
|
.attr('cy', yValue + barHeight / 2)
|
|
2604
|
-
.attr('r',
|
|
2741
|
+
.attr('r', 0) // Start with 0 radius
|
|
2605
2742
|
.attr('fill', '#3498DB')
|
|
2606
|
-
.attr('stroke', '#
|
|
2607
|
-
.attr('stroke-width',
|
|
2608
|
-
.style('filter', 'drop-shadow(0
|
|
2743
|
+
.attr('stroke', '#ffffff')
|
|
2744
|
+
.attr('stroke-width', 2)
|
|
2745
|
+
.style('filter', 'drop-shadow(0 3px 6px rgba(52, 152, 219, 0.4))')
|
|
2746
|
+
.style('transition', 'all 0.3s ease');
|
|
2747
|
+
// Animate the dot
|
|
2748
|
+
wakeDot.transition()
|
|
2749
|
+
.duration(400)
|
|
2750
|
+
.delay(500 + Math.random() * 100)
|
|
2751
|
+
.ease(d3__namespace.easeBackOut)
|
|
2752
|
+
.attr('r', 6);
|
|
2609
2753
|
}
|
|
2610
2754
|
// Add hover interactions with tooltip
|
|
2611
2755
|
sleepGroup
|
|
@@ -2615,48 +2759,50 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2615
2759
|
.transition()
|
|
2616
2760
|
.duration(200)
|
|
2617
2761
|
.attr('opacity', 1)
|
|
2618
|
-
.style('filter', 'brightness(1.1)');
|
|
2619
|
-
//
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
tooltipContent += `<div>Wake: ${formatTime(wakeHour)}</div>`;
|
|
2762
|
+
.style('filter', 'drop-shadow(0 4px 12px rgba(155, 89, 182, 0.5)) brightness(1.1)');
|
|
2763
|
+
// Scale up the dots
|
|
2764
|
+
d3__namespace.select(this).selectAll('circle')
|
|
2765
|
+
.transition()
|
|
2766
|
+
.duration(200)
|
|
2767
|
+
.attr('r', 8);
|
|
2768
|
+
// Calculate tooltip data
|
|
2769
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
2770
|
+
if (rect) {
|
|
2771
|
+
const duration = (sleepHour !== null && wakeHour !== null)
|
|
2772
|
+
? wakeHour - sleepHour
|
|
2773
|
+
: null;
|
|
2774
|
+
setTooltipData({
|
|
2775
|
+
date: dayData.date,
|
|
2776
|
+
sleepTime: sleepHour !== null ? formatTime(sleepHour) : null,
|
|
2777
|
+
wakeTime: wakeHour !== null ? formatTime(wakeHour) : null,
|
|
2778
|
+
duration: duration !== null ? Math.round(duration * 100) / 100 : null,
|
|
2779
|
+
x: event.pageX,
|
|
2780
|
+
y: event.pageY - 80
|
|
2781
|
+
});
|
|
2639
2782
|
}
|
|
2640
|
-
tooltip.html(tooltipContent)
|
|
2641
|
-
.style('opacity', 1)
|
|
2642
|
-
.style('left', (event.pageX + 10) + 'px')
|
|
2643
|
-
.style('top', (event.pageY - 10) + 'px');
|
|
2644
2783
|
})
|
|
2645
2784
|
.on('mouseleave', function () {
|
|
2646
2785
|
// Reset bar appearance
|
|
2647
2786
|
d3__namespace.select(this).select('rect')
|
|
2648
2787
|
.transition()
|
|
2649
2788
|
.duration(200)
|
|
2650
|
-
.attr('opacity', 0.
|
|
2651
|
-
.style('filter', '
|
|
2789
|
+
.attr('opacity', 0.85)
|
|
2790
|
+
.style('filter', 'drop-shadow(0 2px 8px rgba(155, 89, 182, 0.3))');
|
|
2791
|
+
// Reset dots
|
|
2792
|
+
d3__namespace.select(this).selectAll('circle')
|
|
2793
|
+
.transition()
|
|
2794
|
+
.duration(200)
|
|
2795
|
+
.attr('r', 6);
|
|
2652
2796
|
// Hide tooltip
|
|
2653
|
-
|
|
2797
|
+
setTooltipData(null);
|
|
2654
2798
|
})
|
|
2655
2799
|
.on('mousemove', function (event) {
|
|
2656
2800
|
// Update tooltip position
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2801
|
+
setTooltipData(prev => prev ? {
|
|
2802
|
+
...prev,
|
|
2803
|
+
x: event.pageX,
|
|
2804
|
+
y: event.pageY - 80
|
|
2805
|
+
} : null);
|
|
2660
2806
|
});
|
|
2661
2807
|
});
|
|
2662
2808
|
// Create gradient definition
|
|
@@ -2668,16 +2814,23 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2668
2814
|
gradient.append('stop')
|
|
2669
2815
|
.attr('offset', '0%')
|
|
2670
2816
|
.attr('stop-color', '#9B59B6')
|
|
2671
|
-
.attr('stop-opacity',
|
|
2817
|
+
.attr('stop-opacity', 1);
|
|
2818
|
+
gradient.append('stop')
|
|
2819
|
+
.attr('offset', '50%')
|
|
2820
|
+
.attr('stop-color', '#7B68A6')
|
|
2821
|
+
.attr('stop-opacity', 1);
|
|
2672
2822
|
gradient.append('stop')
|
|
2673
2823
|
.attr('offset', '100%')
|
|
2674
2824
|
.attr('stop-color', '#3498DB')
|
|
2675
|
-
.attr('stop-opacity',
|
|
2825
|
+
.attr('stop-opacity', 1);
|
|
2676
2826
|
const xAxisTicks = d3__namespace.range(18, 43, 3).map(hour => ({
|
|
2677
2827
|
value: hour,
|
|
2678
|
-
label:
|
|
2828
|
+
label: hour === 24 ? '12 AM' : hour === 36 ? '12 PM' :
|
|
2829
|
+
(hour % 24) === 0 ? '12 AM' :
|
|
2830
|
+
(hour % 24) < 12 ? `${hour % 24} AM` :
|
|
2831
|
+
(hour % 24) === 12 ? '12 PM' : `${(hour % 24) - 12} PM`
|
|
2679
2832
|
}));
|
|
2680
|
-
g.append('g')
|
|
2833
|
+
const xAxis = g.append('g')
|
|
2681
2834
|
.attr('class', styles$4.xAxis)
|
|
2682
2835
|
.attr('transform', `translate(0,${chartHeight})`)
|
|
2683
2836
|
.call(d3__namespace.axisBottom(xScale)
|
|
@@ -2685,7 +2838,14 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2685
2838
|
.tickFormat((d) => {
|
|
2686
2839
|
const tick = xAxisTicks.find(t => t.value === d);
|
|
2687
2840
|
return tick ? tick.label : '';
|
|
2688
|
-
})
|
|
2841
|
+
})
|
|
2842
|
+
.tickSizeOuter(0));
|
|
2843
|
+
// Style axis ticks
|
|
2844
|
+
xAxis.selectAll('text')
|
|
2845
|
+
.style('font-weight', d => {
|
|
2846
|
+
const hour = d % 24;
|
|
2847
|
+
return hour === 0 || hour === 12 ? '600' : '400';
|
|
2848
|
+
});
|
|
2689
2849
|
const yAxisTicks = yDomain.filter((_, i) => i % Math.ceil(yDomain.length / 10) === 0);
|
|
2690
2850
|
g.append('g')
|
|
2691
2851
|
.attr('class', styles$4.yAxis)
|
|
@@ -2695,12 +2855,15 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2695
2855
|
const date = new Date(d);
|
|
2696
2856
|
return `${(date.getMonth() + 1).toString().padStart(2, '0')}/${date.getDate().toString().padStart(2, '0')}`;
|
|
2697
2857
|
}));
|
|
2698
|
-
// Cleanup function
|
|
2699
|
-
return () => {
|
|
2700
|
-
tooltip.remove();
|
|
2701
|
-
};
|
|
2702
2858
|
}, [sleepData, chartWidth, chartHeight, margin, onDateClick]);
|
|
2703
|
-
return (jsxRuntime.jsxs("div", { className: styles$4.container, children: [jsxRuntime.jsxs("div", { className: styles$4.header, children: [jsxRuntime.jsx("h3", { className: styles$4.title, children: "Sleep Pattern" }), jsxRuntime.jsxs("div", { className: styles$4.legend, children: [jsxRuntime.jsxs("div", { className: styles$4.legendItem, children: [jsxRuntime.jsx("span", { className: styles$4.sleepDot }), jsxRuntime.jsx("span", { children: "Sleep Time" })] }), jsxRuntime.jsxs("div", { className: styles$4.legendItem, children: [jsxRuntime.jsx("span", { className: styles$4.wakeDot }), jsxRuntime.jsx("span", { children: "Wake Time" })] })] })] }), jsxRuntime.jsx("svg", { ref: svgRef, width: width, height: height, className: styles$4.chart })
|
|
2859
|
+
return (jsxRuntime.jsxs("div", { className: styles$4.container, children: [jsxRuntime.jsxs("div", { className: styles$4.header, children: [jsxRuntime.jsx("h3", { className: styles$4.title, children: "Sleep Pattern" }), jsxRuntime.jsxs("div", { className: styles$4.legend, children: [jsxRuntime.jsxs("div", { className: styles$4.legendItem, children: [jsxRuntime.jsx("span", { className: styles$4.sleepDot }), jsxRuntime.jsx("span", { children: "Sleep Time" })] }), jsxRuntime.jsxs("div", { className: styles$4.legendItem, children: [jsxRuntime.jsx("span", { className: styles$4.wakeDot }), jsxRuntime.jsx("span", { children: "Wake Time" })] })] })] }), jsxRuntime.jsx("svg", { ref: svgRef, width: width, height: height, className: styles$4.chart }), tooltipData && (jsxRuntime.jsxs("div", { className: `${styles$4.tooltip} ${tooltipData ? styles$4.visible : ''}`, style: {
|
|
2860
|
+
left: `${tooltipData.x}px`,
|
|
2861
|
+
top: `${tooltipData.y}px`
|
|
2862
|
+
}, children: [jsxRuntime.jsxs("div", { className: styles$4.tooltipHeader, children: [jsxRuntime.jsx("span", { className: styles$4.tooltipEmoji, children: "\uD83D\uDE34" }), jsxRuntime.jsx("span", { children: new Date(tooltipData.date).toLocaleDateString('en-US', {
|
|
2863
|
+
weekday: 'short',
|
|
2864
|
+
month: 'short',
|
|
2865
|
+
day: 'numeric'
|
|
2866
|
+
}) })] }), jsxRuntime.jsxs("div", { className: styles$4.tooltipInfo, children: [tooltipData.sleepTime && (jsxRuntime.jsxs("div", { className: styles$4.tooltipRow, children: [jsxRuntime.jsxs("span", { className: styles$4.tooltipLabel, children: [jsxRuntime.jsx("span", { children: "\uD83C\uDF19" }), jsxRuntime.jsx("span", { children: "Sleep:" })] }), jsxRuntime.jsx("span", { className: styles$4.tooltipValue, children: tooltipData.sleepTime })] })), tooltipData.wakeTime && (jsxRuntime.jsxs("div", { className: styles$4.tooltipRow, children: [jsxRuntime.jsxs("span", { className: styles$4.tooltipLabel, children: [jsxRuntime.jsx("span", { children: "\u2600\uFE0F" }), jsxRuntime.jsx("span", { children: "Wake:" })] }), jsxRuntime.jsx("span", { className: styles$4.tooltipValue, children: tooltipData.wakeTime })] })), tooltipData.duration !== null && (jsxRuntime.jsxs("div", { className: styles$4.tooltipDuration, children: [jsxRuntime.jsx("span", { children: "\u23F1\uFE0F" }), jsxRuntime.jsxs("span", { children: [Math.floor(tooltipData.duration), "h ", Math.round((tooltipData.duration % 1) * 60), "m"] })] }))] })] }))] }));
|
|
2704
2867
|
};
|
|
2705
2868
|
|
|
2706
2869
|
var styles$3 = {"container":"BooleansHeatmap-module_container__IOyeU","title":"BooleansHeatmap-module_title__8DRRQ","habitEmoji":"BooleansHeatmap-module_habitEmoji__Mawv-","chart":"BooleansHeatmap-module_chart__-q0Pc","monthLabel":"BooleansHeatmap-module_monthLabel__MXbIg","dayLabel":"BooleansHeatmap-module_dayLabel__2RRtm","cell":"BooleansHeatmap-module_cell__WADVB","legend":"BooleansHeatmap-module_legend__WqGF8","legendItem":"BooleansHeatmap-module_legendItem__rDE2g","legendColor":"BooleansHeatmap-module_legendColor__Z34-d","tooltip":"BooleansHeatmap-module_tooltip__-fHl7"};
|
|
@@ -2929,8 +3092,8 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2929
3092
|
const interpolatedArc = d3__namespace.arc()
|
|
2930
3093
|
.startAngle(() => currX0)
|
|
2931
3094
|
.endAngle(() => currX1)
|
|
2932
|
-
.innerRadius(() => Math.sqrt(currY0))
|
|
2933
|
-
.outerRadius(() => Math.sqrt(currY1))
|
|
3095
|
+
.innerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY0))))
|
|
3096
|
+
.outerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY1))))
|
|
2934
3097
|
.cornerRadius(3);
|
|
2935
3098
|
return interpolatedArc(node) || '';
|
|
2936
3099
|
};
|
|
@@ -2960,13 +3123,21 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2960
3123
|
.ease(d3__namespace.easeCubicInOut);
|
|
2961
3124
|
// Zoom to clicked node - scale to fill the entire circle
|
|
2962
3125
|
const clickedOriginal = clickedNode;
|
|
3126
|
+
// Ensure we use the original x values for proper scaling
|
|
3127
|
+
// The x-scale maps the clicked node's angular extent to the full circle
|
|
2963
3128
|
const xScale = d3__namespace.scaleLinear()
|
|
2964
3129
|
.domain([clickedOriginal.x0Original, clickedOriginal.x1Original])
|
|
2965
|
-
.range([0, 2 * Math.PI])
|
|
2966
|
-
|
|
3130
|
+
.range([0, 2 * Math.PI])
|
|
3131
|
+
.clamp(true); // Ensure values stay within bounds
|
|
3132
|
+
// For the radial scale, always use the full radius available
|
|
3133
|
+
// Find the deepest descendant to ensure all children fit
|
|
3134
|
+
const descendants = nodes.filter(d => isParentOf(clickedNode, d));
|
|
3135
|
+
const maxChildRadius = Math.max(...descendants.map(d => d.y1Original));
|
|
3136
|
+
// Always scale to use the full radius, regardless of original size
|
|
2967
3137
|
const yScale = d3__namespace.scaleLinear()
|
|
2968
|
-
.domain([clickedOriginal.y0Original,
|
|
2969
|
-
.range([0, radius * radius])
|
|
3138
|
+
.domain([clickedOriginal.y0Original, maxChildRadius])
|
|
3139
|
+
.range([0, radius * radius])
|
|
3140
|
+
.clamp(true); // Ensure values stay within bounds
|
|
2970
3141
|
// First, immediately hide elements that shouldn't be visible
|
|
2971
3142
|
paths.style('opacity', (d) => isParentOf(clickedNode, d) ? 1 : 0);
|
|
2972
3143
|
paths.transition(transition)
|
|
@@ -2978,17 +3149,33 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2978
3149
|
return () => '';
|
|
2979
3150
|
}
|
|
2980
3151
|
// Calculate new positions based on the original positions
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
3152
|
+
// ALWAYS scale to fill the full circle, no minimum size preservation
|
|
3153
|
+
let newX0 = xScale(node.x0Original);
|
|
3154
|
+
let newX1 = xScale(node.x1Original);
|
|
3155
|
+
// Ensure proper scaling even for very small segments
|
|
3156
|
+
// The clicked node's extent should always map to full circle
|
|
3157
|
+
if (newX1 <= newX0) {
|
|
3158
|
+
// Force proper angular extent based on proportion
|
|
3159
|
+
const proportion = (node.x1Original - node.x0Original) / (clickedOriginal.x1Original - clickedOriginal.x0Original);
|
|
3160
|
+
newX1 = newX0 + (2 * Math.PI * proportion);
|
|
3161
|
+
}
|
|
3162
|
+
// For radial positions, scale appropriately
|
|
2984
3163
|
let newY0, newY1;
|
|
2985
3164
|
if (node === clickedNode) {
|
|
2986
|
-
// The clicked node itself should fill from center
|
|
3165
|
+
// The clicked node itself should fill from center
|
|
2987
3166
|
newY0 = 0;
|
|
2988
|
-
|
|
3167
|
+
// If it has children, leave room for them; otherwise fill to edge
|
|
3168
|
+
if (node.children && node.children.length > 0) {
|
|
3169
|
+
const childrenY0 = Math.min(...node.children.map(c => c.y0Original));
|
|
3170
|
+
newY1 = yScale(childrenY0) * 0.98; // Very slight gap for visual separation
|
|
3171
|
+
}
|
|
3172
|
+
else {
|
|
3173
|
+
newY1 = radius * radius * 0.98; // Fill almost to edge
|
|
3174
|
+
}
|
|
2989
3175
|
}
|
|
2990
3176
|
else {
|
|
2991
|
-
// Child nodes should be scaled
|
|
3177
|
+
// Child nodes should be scaled to fill the remaining space proportionally
|
|
3178
|
+
// NO minimum size enforcement - pure proportional scaling
|
|
2992
3179
|
newY0 = yScale(node.y0Original);
|
|
2993
3180
|
newY1 = yScale(node.y1Original);
|
|
2994
3181
|
}
|
|
@@ -3011,8 +3198,8 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
3011
3198
|
const interpolatedArc = d3__namespace.arc()
|
|
3012
3199
|
.startAngle(() => currX0)
|
|
3013
3200
|
.endAngle(() => currX1)
|
|
3014
|
-
.innerRadius(() => Math.sqrt(currY0))
|
|
3015
|
-
.outerRadius(() => Math.sqrt(currY1))
|
|
3201
|
+
.innerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY0))))
|
|
3202
|
+
.outerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY1))))
|
|
3016
3203
|
.cornerRadius(3);
|
|
3017
3204
|
return interpolatedArc(node) || '';
|
|
3018
3205
|
};
|
|
@@ -3048,11 +3235,11 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
3048
3235
|
return getColor(ancestor.data.name, d.depth);
|
|
3049
3236
|
})
|
|
3050
3237
|
.attr('stroke', () => {
|
|
3051
|
-
// Get computed
|
|
3238
|
+
// Get computed border color from theme
|
|
3052
3239
|
const computedStyle = window.getComputedStyle(svgRef.current);
|
|
3053
|
-
return computedStyle.getPropertyValue('--
|
|
3240
|
+
return computedStyle.getPropertyValue('--color-border') || '#e2e8f0';
|
|
3054
3241
|
})
|
|
3055
|
-
.attr('stroke-width',
|
|
3242
|
+
.attr('stroke-width', 2)
|
|
3056
3243
|
.style('cursor', d => (!d.children || d.children.length === 0) ? 'default' : 'pointer')
|
|
3057
3244
|
.on('click', handleClick)
|
|
3058
3245
|
.on('mouseover', function (event, d) {
|
|
@@ -3112,7 +3299,9 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
3112
3299
|
});
|
|
3113
3300
|
const shouldDisplayLabel = (d) => {
|
|
3114
3301
|
const angle = d.x1 - d.x0;
|
|
3115
|
-
|
|
3302
|
+
// When zoomed in, be more lenient with label display since segments are larger
|
|
3303
|
+
const minAngle = focusedNodeRef.current ? 0.05 : 0.15;
|
|
3304
|
+
return angle > minAngle && d.depth <= 2;
|
|
3116
3305
|
};
|
|
3117
3306
|
// Function to update labels after transitions
|
|
3118
3307
|
const updateLabels = (transition, visibleNodes, _isReset) => {
|