mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +00:00
109 lines
3.0 KiB
Dart
109 lines
3.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/heat_map_tooltip.dart';
|
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_painter.dart';
|
|
|
|
class InteractiveHeatMap extends StatefulWidget {
|
|
const InteractiveHeatMap({
|
|
required this.items,
|
|
required this.maxValue,
|
|
required this.cellSize,
|
|
super.key,
|
|
});
|
|
|
|
final List<OccupancyPaintItem> items;
|
|
final int maxValue;
|
|
final double cellSize;
|
|
|
|
@override
|
|
State<InteractiveHeatMap> createState() => _InteractiveHeatMapState();
|
|
}
|
|
|
|
class _InteractiveHeatMapState extends State<InteractiveHeatMap> {
|
|
OccupancyPaintItem? _hoveredItem;
|
|
OverlayEntry? _overlayEntry;
|
|
final LayerLink _layerLink = LayerLink();
|
|
|
|
@override
|
|
void dispose() {
|
|
_removeOverlay();
|
|
_overlayEntry?.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _removeOverlay() {
|
|
_overlayEntry?.remove();
|
|
_overlayEntry = null;
|
|
}
|
|
|
|
void _showTooltip(OccupancyPaintItem item, Offset localPosition) {
|
|
_removeOverlay();
|
|
|
|
final column = item.index ~/ 7;
|
|
final row = item.index % 7;
|
|
final x = column * widget.cellSize;
|
|
final y = row * widget.cellSize;
|
|
|
|
_overlayEntry = OverlayEntry(
|
|
builder: (context) => Positioned(
|
|
child: CompositedTransformFollower(
|
|
link: _layerLink,
|
|
offset: Offset(x + widget.cellSize, y),
|
|
child: Material(
|
|
color: Colors.transparent,
|
|
child: Transform.translate(
|
|
offset: Offset(-(widget.cellSize * 2.5), -50),
|
|
child: HeatMapTooltip(date: item.date.toUtc(), value: item.value),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
Overlay.of(context).insert(_overlayEntry!);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return CompositedTransformTarget(
|
|
link: _layerLink,
|
|
child: MouseRegion(
|
|
onHover: (event) {
|
|
final column = event.localPosition.dx ~/ widget.cellSize;
|
|
final row = event.localPosition.dy ~/ widget.cellSize;
|
|
final index = column * 7 + row;
|
|
|
|
if (index >= 0 && index < widget.items.length) {
|
|
final item = widget.items[index];
|
|
if (_hoveredItem != item) {
|
|
setState(() => _hoveredItem = item);
|
|
_showTooltip(item, event.localPosition);
|
|
}
|
|
} else {
|
|
_removeOverlay();
|
|
setState(() => _hoveredItem = null);
|
|
}
|
|
},
|
|
onExit: (_) {
|
|
_removeOverlay();
|
|
setState(() => _hoveredItem = null);
|
|
},
|
|
child: CustomPaint(
|
|
isComplex: true,
|
|
size: _painterSize,
|
|
painter: OccupancyPainter(
|
|
items: widget.items,
|
|
maxValue: widget.maxValue,
|
|
hoveredItem: _hoveredItem,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Size get _painterSize {
|
|
final height = 7 * widget.cellSize;
|
|
final width = widget.items.length ~/ 7 * widget.cellSize;
|
|
return Size(width, height);
|
|
}
|
|
}
|