@stfrigerio/sito-template 0.1.21 → 0.1.22
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/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 +151 -59
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +151 -59
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SleepChart.d.ts","sourceRoot":"","sources":["../../../../../src/components/organisms/charts/SleepChart/SleepChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqC,MAAM,OAAO,CAAA;AAIzD,UAAU,SAAS;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,UAAU,eAAe;IACrB,SAAS,EAAE,SAAS,EAAE,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC;AAOD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,
|
|
1
|
+
{"version":3,"file":"SleepChart.d.ts","sourceRoot":"","sources":["../../../../../src/components/organisms/charts/SleepChart/SleepChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqC,MAAM,OAAO,CAAA;AAIzD,UAAU,SAAS;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,UAAU,eAAe;IACrB,SAAS,EAAE,SAAS,EAAE,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC;AAOD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA6UhD,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SunburstChart.d.ts","sourceRoot":"","sources":["../../../../../src/components/organisms/charts/SunburstChart/SunburstChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAI7D,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;CAC9B;AAED,UAAU,kBAAkB;IACxB,IAAI,EAAE,cAAc,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAgCD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"SunburstChart.d.ts","sourceRoot":"","sources":["../../../../../src/components/organisms/charts/SunburstChart/SunburstChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAI7D,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;CAC9B;AAED,UAAU,kBAAkB;IACxB,IAAI,EAAE,cAAc,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAgCD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA4jBtD,CAAA"}
|
package/dist/index.esm.js
CHANGED
|
@@ -2473,9 +2473,9 @@ const parseTimeToDecimal = (time) => {
|
|
|
2473
2473
|
const [hours, minutes] = time.split(':').map(Number);
|
|
2474
2474
|
return hours + minutes / 60;
|
|
2475
2475
|
};
|
|
2476
|
-
const SleepChart = ({ sleepData, width = 800, height =
|
|
2476
|
+
const SleepChart = ({ sleepData, width = 800, height = 400, onDateClick }) => {
|
|
2477
2477
|
const svgRef = useRef(null);
|
|
2478
|
-
const margin = useMemo(() => ({ top:
|
|
2478
|
+
const margin = useMemo(() => ({ top: 30, right: 30, bottom: 50, left: 80 }), []);
|
|
2479
2479
|
const chartWidth = width - margin.left - margin.right;
|
|
2480
2480
|
const chartHeight = height - margin.top - margin.bottom;
|
|
2481
2481
|
useEffect(() => {
|
|
@@ -2489,17 +2489,18 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2489
2489
|
const tooltip = d3.select('body').append('div')
|
|
2490
2490
|
.attr('class', 'sleep-chart-tooltip')
|
|
2491
2491
|
.style('position', 'absolute')
|
|
2492
|
-
.style('background', '
|
|
2493
|
-
.style('color', '
|
|
2494
|
-
.style('padding', '
|
|
2495
|
-
.style('border-radius', '
|
|
2496
|
-
.style('box-shadow', '0
|
|
2497
|
-
.style('border', '1px solid
|
|
2492
|
+
.style('background', 'linear-gradient(135deg, rgba(155, 89, 182, 0.95), rgba(52, 152, 219, 0.95))')
|
|
2493
|
+
.style('color', '#ffffff')
|
|
2494
|
+
.style('padding', '16px')
|
|
2495
|
+
.style('border-radius', '12px')
|
|
2496
|
+
.style('box-shadow', '0 8px 24px rgba(0,0,0,0.3)')
|
|
2497
|
+
.style('border', '1px solid rgba(255,255,255,0.2)')
|
|
2498
2498
|
.style('font-size', '14px')
|
|
2499
2499
|
.style('pointer-events', 'none')
|
|
2500
2500
|
.style('opacity', 0)
|
|
2501
2501
|
.style('z-index', '1000')
|
|
2502
|
-
.style('backdrop-filter', 'blur(
|
|
2502
|
+
.style('backdrop-filter', 'blur(12px)')
|
|
2503
|
+
.style('min-width', '180px');
|
|
2503
2504
|
const xScale = d3.scaleLinear()
|
|
2504
2505
|
.domain([18, 42])
|
|
2505
2506
|
.range([0, chartWidth]);
|
|
@@ -2507,16 +2508,39 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2507
2508
|
const yScale = d3.scaleBand()
|
|
2508
2509
|
.domain(yDomain)
|
|
2509
2510
|
.range([0, chartHeight])
|
|
2510
|
-
.paddingInner(0.
|
|
2511
|
-
.paddingOuter(0.
|
|
2511
|
+
.paddingInner(0.2)
|
|
2512
|
+
.paddingOuter(0.1);
|
|
2513
|
+
// Add vertical grid lines with midnight/noon highlights
|
|
2512
2514
|
g.selectAll('.grid-line-x')
|
|
2513
|
-
.data(d3.range(18, 43,
|
|
2515
|
+
.data(d3.range(18, 43, 1))
|
|
2514
2516
|
.enter().append('line')
|
|
2515
2517
|
.attr('class', styles$4.gridLine)
|
|
2516
2518
|
.attr('x1', d => xScale(d))
|
|
2517
2519
|
.attr('y1', 0)
|
|
2518
2520
|
.attr('x2', d => xScale(d))
|
|
2519
|
-
.attr('y2', chartHeight)
|
|
2521
|
+
.attr('y2', chartHeight)
|
|
2522
|
+
.style('stroke', d => {
|
|
2523
|
+
const hour = d % 24;
|
|
2524
|
+
if (hour === 0)
|
|
2525
|
+
return 'var(--color-border)'; // Midnight
|
|
2526
|
+
if (hour === 12)
|
|
2527
|
+
return 'var(--color-border)'; // Noon
|
|
2528
|
+
return 'var(--color-border)';
|
|
2529
|
+
})
|
|
2530
|
+
.style('stroke-opacity', d => {
|
|
2531
|
+
const hour = d % 24;
|
|
2532
|
+
if (hour === 0 || hour === 12)
|
|
2533
|
+
return 0.3;
|
|
2534
|
+
if (hour % 3 === 0)
|
|
2535
|
+
return 0.15;
|
|
2536
|
+
return 0.05;
|
|
2537
|
+
})
|
|
2538
|
+
.style('stroke-width', d => {
|
|
2539
|
+
const hour = d % 24;
|
|
2540
|
+
if (hour === 0 || hour === 12)
|
|
2541
|
+
return 2;
|
|
2542
|
+
return 1;
|
|
2543
|
+
});
|
|
2520
2544
|
sleepData.forEach((dayData) => {
|
|
2521
2545
|
const yValue = yScale(dayData.date);
|
|
2522
2546
|
if (yValue === undefined)
|
|
@@ -2554,37 +2578,60 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2554
2578
|
}
|
|
2555
2579
|
// Draw the bar only if both values exist
|
|
2556
2580
|
if (sleepHour !== null && wakeHour !== null) {
|
|
2557
|
-
sleepGroup.append('rect')
|
|
2581
|
+
const rect = sleepGroup.append('rect')
|
|
2558
2582
|
.attr('x', xScale(sleepHour))
|
|
2559
2583
|
.attr('y', yValue)
|
|
2560
|
-
.attr('width',
|
|
2584
|
+
.attr('width', 0) // Start with 0 width for animation
|
|
2561
2585
|
.attr('height', barHeight)
|
|
2562
|
-
.attr('rx',
|
|
2586
|
+
.attr('rx', barHeight / 2)
|
|
2563
2587
|
.attr('fill', 'url(#sleepGradient)')
|
|
2564
|
-
.attr('opacity', 0
|
|
2565
|
-
.attr('stroke', '
|
|
2588
|
+
.attr('opacity', 0) // Start invisible
|
|
2589
|
+
.attr('stroke', 'rgba(255,255,255,0.3)')
|
|
2590
|
+
.attr('stroke-width', 1)
|
|
2591
|
+
.style('filter', 'drop-shadow(0 2px 8px rgba(155, 89, 182, 0.3))');
|
|
2592
|
+
// Animate the bar
|
|
2593
|
+
rect.transition()
|
|
2594
|
+
.duration(800)
|
|
2595
|
+
.delay(Math.random() * 200) // Stagger the animations
|
|
2596
|
+
.ease(d3.easeCubicOut)
|
|
2597
|
+
.attr('width', Math.max(0, xScale(wakeHour) - xScale(sleepHour)))
|
|
2598
|
+
.attr('opacity', 0.85);
|
|
2566
2599
|
}
|
|
2567
2600
|
// Draw sleep dot if sleep time exists
|
|
2568
2601
|
if (sleepHour !== null) {
|
|
2569
|
-
sleepGroup.append('circle')
|
|
2602
|
+
const sleepDot = sleepGroup.append('circle')
|
|
2570
2603
|
.attr('cx', xScale(sleepHour))
|
|
2571
2604
|
.attr('cy', yValue + barHeight / 2)
|
|
2572
|
-
.attr('r',
|
|
2605
|
+
.attr('r', 0) // Start with 0 radius
|
|
2573
2606
|
.attr('fill', '#9B59B6')
|
|
2574
|
-
.attr('stroke', '#
|
|
2575
|
-
.attr('stroke-width',
|
|
2576
|
-
.style('filter', 'drop-shadow(0
|
|
2607
|
+
.attr('stroke', '#ffffff')
|
|
2608
|
+
.attr('stroke-width', 2)
|
|
2609
|
+
.style('filter', 'drop-shadow(0 3px 6px rgba(155, 89, 182, 0.4))')
|
|
2610
|
+
.style('transition', 'all 0.3s ease');
|
|
2611
|
+
// Animate the dot
|
|
2612
|
+
sleepDot.transition()
|
|
2613
|
+
.duration(400)
|
|
2614
|
+
.delay(300 + Math.random() * 100)
|
|
2615
|
+
.ease(d3.easeBackOut)
|
|
2616
|
+
.attr('r', 6);
|
|
2577
2617
|
}
|
|
2578
2618
|
// Draw wake dot if wake hour exists
|
|
2579
2619
|
if (wakeHour !== null) {
|
|
2580
|
-
sleepGroup.append('circle')
|
|
2620
|
+
const wakeDot = sleepGroup.append('circle')
|
|
2581
2621
|
.attr('cx', xScale(wakeHour))
|
|
2582
2622
|
.attr('cy', yValue + barHeight / 2)
|
|
2583
|
-
.attr('r',
|
|
2623
|
+
.attr('r', 0) // Start with 0 radius
|
|
2584
2624
|
.attr('fill', '#3498DB')
|
|
2585
|
-
.attr('stroke', '#
|
|
2586
|
-
.attr('stroke-width',
|
|
2587
|
-
.style('filter', 'drop-shadow(0
|
|
2625
|
+
.attr('stroke', '#ffffff')
|
|
2626
|
+
.attr('stroke-width', 2)
|
|
2627
|
+
.style('filter', 'drop-shadow(0 3px 6px rgba(52, 152, 219, 0.4))')
|
|
2628
|
+
.style('transition', 'all 0.3s ease');
|
|
2629
|
+
// Animate the dot
|
|
2630
|
+
wakeDot.transition()
|
|
2631
|
+
.duration(400)
|
|
2632
|
+
.delay(500 + Math.random() * 100)
|
|
2633
|
+
.ease(d3.easeBackOut)
|
|
2634
|
+
.attr('r', 6);
|
|
2588
2635
|
}
|
|
2589
2636
|
// Add hover interactions with tooltip
|
|
2590
2637
|
sleepGroup
|
|
@@ -2594,7 +2641,12 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2594
2641
|
.transition()
|
|
2595
2642
|
.duration(200)
|
|
2596
2643
|
.attr('opacity', 1)
|
|
2597
|
-
.style('filter', 'brightness(1.1)');
|
|
2644
|
+
.style('filter', 'drop-shadow(0 4px 12px rgba(155, 89, 182, 0.5)) brightness(1.1)');
|
|
2645
|
+
// Scale up the dots
|
|
2646
|
+
d3.select(this).selectAll('circle')
|
|
2647
|
+
.transition()
|
|
2648
|
+
.duration(200)
|
|
2649
|
+
.attr('r', 8);
|
|
2598
2650
|
// Show tooltip
|
|
2599
2651
|
const formatTime = (hour) => {
|
|
2600
2652
|
const h24 = hour % 24;
|
|
@@ -2603,18 +2655,18 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2603
2655
|
const minutes = Math.round((hour % 1) * 60);
|
|
2604
2656
|
return `${h12}:${minutes.toString().padStart(2, '0')} ${ampm}`;
|
|
2605
2657
|
};
|
|
2606
|
-
let tooltipContent = `<div
|
|
2658
|
+
let tooltipContent = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 16px;">${new Date(dayData.date).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })}</div>`;
|
|
2607
2659
|
if (sleepHour !== null && wakeHour !== null) {
|
|
2608
2660
|
const sleepDuration = wakeHour - sleepHour;
|
|
2609
|
-
tooltipContent += `<div
|
|
2610
|
-
tooltipContent += `<div
|
|
2611
|
-
tooltipContent += `<div
|
|
2661
|
+
tooltipContent += `<div style="margin: 4px 0;">🌙 Sleep: ${formatTime(sleepHour)}</div>`;
|
|
2662
|
+
tooltipContent += `<div style="margin: 4px 0;">☀️ Wake: ${formatTime(wakeHour)}</div>`;
|
|
2663
|
+
tooltipContent += `<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,0.2);">⏱️ Duration: ${Math.floor(sleepDuration)}h ${Math.round((sleepDuration % 1) * 60)}m</div>`;
|
|
2612
2664
|
}
|
|
2613
2665
|
else {
|
|
2614
2666
|
if (sleepHour !== null)
|
|
2615
|
-
tooltipContent += `<div
|
|
2667
|
+
tooltipContent += `<div style="margin: 4px 0;">🌙 Sleep: ${formatTime(sleepHour)}</div>`;
|
|
2616
2668
|
if (wakeHour !== null)
|
|
2617
|
-
tooltipContent += `<div
|
|
2669
|
+
tooltipContent += `<div style="margin: 4px 0;">☀️ Wake: ${formatTime(wakeHour)}</div>`;
|
|
2618
2670
|
}
|
|
2619
2671
|
tooltip.html(tooltipContent)
|
|
2620
2672
|
.style('opacity', 1)
|
|
@@ -2626,8 +2678,13 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2626
2678
|
d3.select(this).select('rect')
|
|
2627
2679
|
.transition()
|
|
2628
2680
|
.duration(200)
|
|
2629
|
-
.attr('opacity', 0.
|
|
2630
|
-
.style('filter', '
|
|
2681
|
+
.attr('opacity', 0.85)
|
|
2682
|
+
.style('filter', 'drop-shadow(0 2px 8px rgba(155, 89, 182, 0.3))');
|
|
2683
|
+
// Reset dots
|
|
2684
|
+
d3.select(this).selectAll('circle')
|
|
2685
|
+
.transition()
|
|
2686
|
+
.duration(200)
|
|
2687
|
+
.attr('r', 6);
|
|
2631
2688
|
// Hide tooltip
|
|
2632
2689
|
tooltip.style('opacity', 0);
|
|
2633
2690
|
})
|
|
@@ -2647,16 +2704,23 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2647
2704
|
gradient.append('stop')
|
|
2648
2705
|
.attr('offset', '0%')
|
|
2649
2706
|
.attr('stop-color', '#9B59B6')
|
|
2650
|
-
.attr('stop-opacity',
|
|
2707
|
+
.attr('stop-opacity', 1);
|
|
2708
|
+
gradient.append('stop')
|
|
2709
|
+
.attr('offset', '50%')
|
|
2710
|
+
.attr('stop-color', '#7B68A6')
|
|
2711
|
+
.attr('stop-opacity', 1);
|
|
2651
2712
|
gradient.append('stop')
|
|
2652
2713
|
.attr('offset', '100%')
|
|
2653
2714
|
.attr('stop-color', '#3498DB')
|
|
2654
|
-
.attr('stop-opacity',
|
|
2715
|
+
.attr('stop-opacity', 1);
|
|
2655
2716
|
const xAxisTicks = d3.range(18, 43, 3).map(hour => ({
|
|
2656
2717
|
value: hour,
|
|
2657
|
-
label:
|
|
2718
|
+
label: hour === 24 ? '12 AM' : hour === 36 ? '12 PM' :
|
|
2719
|
+
(hour % 24) === 0 ? '12 AM' :
|
|
2720
|
+
(hour % 24) < 12 ? `${hour % 24} AM` :
|
|
2721
|
+
(hour % 24) === 12 ? '12 PM' : `${(hour % 24) - 12} PM`
|
|
2658
2722
|
}));
|
|
2659
|
-
g.append('g')
|
|
2723
|
+
const xAxis = g.append('g')
|
|
2660
2724
|
.attr('class', styles$4.xAxis)
|
|
2661
2725
|
.attr('transform', `translate(0,${chartHeight})`)
|
|
2662
2726
|
.call(d3.axisBottom(xScale)
|
|
@@ -2664,7 +2728,14 @@ const SleepChart = ({ sleepData, width = 800, height = 300, onDateClick }) => {
|
|
|
2664
2728
|
.tickFormat((d) => {
|
|
2665
2729
|
const tick = xAxisTicks.find(t => t.value === d);
|
|
2666
2730
|
return tick ? tick.label : '';
|
|
2667
|
-
})
|
|
2731
|
+
})
|
|
2732
|
+
.tickSizeOuter(0));
|
|
2733
|
+
// Style axis ticks
|
|
2734
|
+
xAxis.selectAll('text')
|
|
2735
|
+
.style('font-weight', d => {
|
|
2736
|
+
const hour = d % 24;
|
|
2737
|
+
return hour === 0 || hour === 12 ? '600' : '400';
|
|
2738
|
+
});
|
|
2668
2739
|
const yAxisTicks = yDomain.filter((_, i) => i % Math.ceil(yDomain.length / 10) === 0);
|
|
2669
2740
|
g.append('g')
|
|
2670
2741
|
.attr('class', styles$4.yAxis)
|
|
@@ -2908,8 +2979,8 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2908
2979
|
const interpolatedArc = d3.arc()
|
|
2909
2980
|
.startAngle(() => currX0)
|
|
2910
2981
|
.endAngle(() => currX1)
|
|
2911
|
-
.innerRadius(() => Math.sqrt(currY0))
|
|
2912
|
-
.outerRadius(() => Math.sqrt(currY1))
|
|
2982
|
+
.innerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY0))))
|
|
2983
|
+
.outerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY1))))
|
|
2913
2984
|
.cornerRadius(3);
|
|
2914
2985
|
return interpolatedArc(node) || '';
|
|
2915
2986
|
};
|
|
@@ -2939,13 +3010,20 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2939
3010
|
.ease(d3.easeCubicInOut);
|
|
2940
3011
|
// Zoom to clicked node - scale to fill the entire circle
|
|
2941
3012
|
const clickedOriginal = clickedNode;
|
|
3013
|
+
// Ensure we use the original x values for proper scaling
|
|
3014
|
+
// The x-scale maps the clicked node's angular extent to the full circle
|
|
2942
3015
|
const xScale = d3.scaleLinear()
|
|
2943
3016
|
.domain([clickedOriginal.x0Original, clickedOriginal.x1Original])
|
|
2944
|
-
.range([0, 2 * Math.PI])
|
|
2945
|
-
|
|
3017
|
+
.range([0, 2 * Math.PI])
|
|
3018
|
+
.clamp(true); // Ensure values stay within bounds
|
|
3019
|
+
// For the radial scale, we want to keep the children visible
|
|
3020
|
+
// Map from the clicked node's inner radius to the maximum child radius
|
|
3021
|
+
const maxChildRadius = Math.max(clickedOriginal.y1Original, ...nodes.filter(d => isParentOf(clickedNode, d) && d !== clickedNode)
|
|
3022
|
+
.map(d => d.y1Original));
|
|
2946
3023
|
const yScale = d3.scaleLinear()
|
|
2947
|
-
.domain([clickedOriginal.y0Original,
|
|
2948
|
-
.range([0, radius * radius])
|
|
3024
|
+
.domain([clickedOriginal.y0Original, maxChildRadius])
|
|
3025
|
+
.range([0, radius * radius])
|
|
3026
|
+
.clamp(true); // Ensure values stay within bounds
|
|
2949
3027
|
// First, immediately hide elements that shouldn't be visible
|
|
2950
3028
|
paths.style('opacity', (d) => isParentOf(clickedNode, d) ? 1 : 0);
|
|
2951
3029
|
paths.transition(transition)
|
|
@@ -2957,17 +3035,31 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2957
3035
|
return () => '';
|
|
2958
3036
|
}
|
|
2959
3037
|
// Calculate new positions based on the original positions
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
3038
|
+
// Ensure angular values wrap correctly and don't get stuck
|
|
3039
|
+
let newX0 = xScale(node.x0Original);
|
|
3040
|
+
let newX1 = xScale(node.x1Original);
|
|
3041
|
+
// Handle edge case where the scaled values might be equal or inverted
|
|
3042
|
+
if (Math.abs(newX1 - newX0) < 0.001) {
|
|
3043
|
+
// If the segment would be too small, give it a minimum size
|
|
3044
|
+
newX1 = newX0 + 0.001;
|
|
3045
|
+
}
|
|
3046
|
+
// Ensure the angles span correctly (handle wraparound)
|
|
3047
|
+
if (newX1 < newX0) {
|
|
3048
|
+
newX1 = newX0 + (2 * Math.PI * (node.x1Original - node.x0Original) / (clickedOriginal.x1Original - clickedOriginal.x0Original));
|
|
3049
|
+
}
|
|
3050
|
+
// For radial positions, scale appropriately
|
|
2963
3051
|
let newY0, newY1;
|
|
2964
3052
|
if (node === clickedNode) {
|
|
2965
|
-
// The clicked node itself should fill from center to
|
|
3053
|
+
// The clicked node itself should fill from center to where its children start
|
|
2966
3054
|
newY0 = 0;
|
|
2967
|
-
|
|
3055
|
+
// Find the minimum y0 of its direct children to determine where to end
|
|
3056
|
+
const childrenY0 = node.children ?
|
|
3057
|
+
Math.min(...node.children.map(c => c.y0Original)) :
|
|
3058
|
+
clickedOriginal.y1Original;
|
|
3059
|
+
newY1 = yScale(childrenY0) * 0.95; // Slightly smaller to create visual separation
|
|
2968
3060
|
}
|
|
2969
3061
|
else {
|
|
2970
|
-
// Child nodes should be scaled within the
|
|
3062
|
+
// Child nodes should be scaled within the remaining space
|
|
2971
3063
|
newY0 = yScale(node.y0Original);
|
|
2972
3064
|
newY1 = yScale(node.y1Original);
|
|
2973
3065
|
}
|
|
@@ -2990,8 +3082,8 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
2990
3082
|
const interpolatedArc = d3.arc()
|
|
2991
3083
|
.startAngle(() => currX0)
|
|
2992
3084
|
.endAngle(() => currX1)
|
|
2993
|
-
.innerRadius(() => Math.sqrt(currY0))
|
|
2994
|
-
.outerRadius(() => Math.sqrt(currY1))
|
|
3085
|
+
.innerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY0))))
|
|
3086
|
+
.outerRadius(() => Math.min(radius, Math.sqrt(Math.max(0, currY1))))
|
|
2995
3087
|
.cornerRadius(3);
|
|
2996
3088
|
return interpolatedArc(node) || '';
|
|
2997
3089
|
};
|
|
@@ -3027,11 +3119,11 @@ const SunburstChart = ({ data, width = 500, height = 500, title = 'Sunburst Char
|
|
|
3027
3119
|
return getColor(ancestor.data.name, d.depth);
|
|
3028
3120
|
})
|
|
3029
3121
|
.attr('stroke', () => {
|
|
3030
|
-
// Get computed
|
|
3122
|
+
// Get computed border color from theme
|
|
3031
3123
|
const computedStyle = window.getComputedStyle(svgRef.current);
|
|
3032
|
-
return computedStyle.getPropertyValue('--
|
|
3124
|
+
return computedStyle.getPropertyValue('--color-border') || '#e2e8f0';
|
|
3033
3125
|
})
|
|
3034
|
-
.attr('stroke-width',
|
|
3126
|
+
.attr('stroke-width', 2)
|
|
3035
3127
|
.style('cursor', d => (!d.children || d.children.length === 0) ? 'default' : 'pointer')
|
|
3036
3128
|
.on('click', handleClick)
|
|
3037
3129
|
.on('mouseover', function (event, d) {
|