mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-25 23:39:41 +00:00
Compare commits
2 Commits
de5d8df01c
...
b223194950
Author | SHA1 | Date | |
---|---|---|---|
b223194950 | |||
466f5b89c7 |
@ -5,13 +5,14 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
class SpacesConnectionsArrowPainter extends CustomPainter {
|
||||
final List<SpaceConnectionModel> connections;
|
||||
final Map<String, Offset> positions;
|
||||
final double cardWidth = 150.0;
|
||||
final Map<String, double> cardWidths;
|
||||
final double cardHeight = 90.0;
|
||||
final Set<String> highlightedUuids;
|
||||
|
||||
SpacesConnectionsArrowPainter({
|
||||
required this.connections,
|
||||
required this.positions,
|
||||
required this.cardWidths,
|
||||
required this.highlightedUuids,
|
||||
});
|
||||
|
||||
@ -29,19 +30,30 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
||||
|
||||
final from = positions[connection.from];
|
||||
final to = positions[connection.to];
|
||||
final fromWidth = cardWidths[connection.from] ?? 150.0;
|
||||
final toWidth = cardWidths[connection.to] ?? 150.0;
|
||||
|
||||
if (from != null && to != null) {
|
||||
final startPoint =
|
||||
Offset(from.dx + cardWidth / 2, from.dy + cardHeight - 10);
|
||||
final endPoint = Offset(to.dx + cardWidth / 2, to.dy);
|
||||
Offset(from.dx + fromWidth / 2, from.dy + cardHeight - 10);
|
||||
final endPoint = Offset(to.dx + toWidth / 2, to.dy);
|
||||
|
||||
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
||||
|
||||
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 100);
|
||||
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 100);
|
||||
|
||||
path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx,
|
||||
controlPoint2.dy, endPoint.dx, endPoint.dy);
|
||||
if ((startPoint.dx - endPoint.dx).abs() < 1.0) {
|
||||
path.lineTo(endPoint.dx, endPoint.dy);
|
||||
} else {
|
||||
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 100);
|
||||
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 100);
|
||||
path.cubicTo(
|
||||
controlPoint1.dx,
|
||||
controlPoint1.dy,
|
||||
controlPoint2.dx,
|
||||
controlPoint2.dy,
|
||||
endPoint.dx,
|
||||
endPoint.dy,
|
||||
);
|
||||
}
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
|
||||
@ -51,7 +63,7 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
||||
..style = PaintingStyle.fill
|
||||
..blendMode = BlendMode.srcIn;
|
||||
canvas.drawCircle(endPoint, 4, circlePaint);
|
||||
canvas.drawCircle(endPoint, 6, circlePaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,10 +30,11 @@ class CommunityStructureCanvas extends StatefulWidget {
|
||||
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final Map<String, Offset> _positions = {};
|
||||
final double _cardWidth = 150.0;
|
||||
final Map<String, double> _cardWidths = {};
|
||||
final double _cardHeight = 90.0;
|
||||
final double _horizontalSpacing = 150.0;
|
||||
final double _verticalSpacing = 120.0;
|
||||
static const double _minCardWidth = 150.0;
|
||||
|
||||
late final TransformationController _transformationController;
|
||||
late final AnimationController _animationController;
|
||||
@ -51,6 +52,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
@ -58,7 +60,6 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
}
|
||||
});
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -68,6 +69,34 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
double _calculateCardWidth(String text) {
|
||||
final style = context.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(text: text, style: style),
|
||||
maxLines: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
const iconWidth = 40.0;
|
||||
const horizontalPadding = 10.0;
|
||||
const contentPadding = 10.0;
|
||||
final calculatedWidth =
|
||||
iconWidth + horizontalPadding + textPainter.width + contentPadding;
|
||||
|
||||
return calculatedWidth.clamp(_minCardWidth, double.infinity);
|
||||
}
|
||||
|
||||
void _calculateAllCardWidths(List<SpaceModel> spaces) {
|
||||
for (final space in spaces) {
|
||||
_cardWidths[space.uuid] = _calculateCardWidth(space.spaceName);
|
||||
if (space.children.isNotEmpty) {
|
||||
_calculateAllCardWidths(space.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> _getAllDescendantUuids(SpaceModel space) {
|
||||
final uuids = <String>{};
|
||||
for (final child in space.children) {
|
||||
@ -102,11 +131,12 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
final position = _positions[space.uuid];
|
||||
if (position == null) return;
|
||||
|
||||
final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth;
|
||||
const scale = 1;
|
||||
final viewSize = context.size;
|
||||
if (viewSize == null) return;
|
||||
|
||||
final x = -position.dx * scale + (viewSize.width / 2) - (_cardWidth * scale / 2);
|
||||
final x = -position.dx * scale + (viewSize.width / 2) - (cardWidth * scale / 2);
|
||||
final y =
|
||||
-position.dy * scale + (viewSize.height / 2) - (_cardHeight * scale / 2);
|
||||
|
||||
@ -155,13 +185,16 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
Map<int, double> levelXOffset,
|
||||
) {
|
||||
for (final space in spaces) {
|
||||
final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth;
|
||||
double childSubtreeWidth = 0;
|
||||
if (space.children.isNotEmpty) {
|
||||
_calculateLayout(space.children, depth + 1, levelXOffset);
|
||||
final firstChildPos = _positions[space.children.first.uuid];
|
||||
final lastChildPos = _positions[space.children.last.uuid];
|
||||
if (firstChildPos != null && lastChildPos != null) {
|
||||
childSubtreeWidth = (lastChildPos.dx + _cardWidth) - firstChildPos.dx;
|
||||
final lastChildWidth =
|
||||
_cardWidths[space.children.last.uuid] ?? _minCardWidth;
|
||||
childSubtreeWidth = (lastChildPos.dx + lastChildWidth) - firstChildPos.dx;
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,7 +203,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
|
||||
if (space.children.isNotEmpty) {
|
||||
final firstChildPos = _positions[space.children.first.uuid]!;
|
||||
x = firstChildPos.dx + (childSubtreeWidth - _cardWidth) / 2;
|
||||
x = firstChildPos.dx + (childSubtreeWidth - cardWidth) / 2;
|
||||
} else {
|
||||
x = currentX;
|
||||
}
|
||||
@ -187,7 +220,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
|
||||
final y = depth * (_verticalSpacing + _cardHeight);
|
||||
_positions[space.uuid] = Offset(x, y);
|
||||
levelXOffset[depth] = x + _cardWidth + _horizontalSpacing;
|
||||
levelXOffset[depth] = x + cardWidth + _horizontalSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,8 +235,11 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
|
||||
List<Widget> _buildTreeWidgets() {
|
||||
_positions.clear();
|
||||
_cardWidths.clear();
|
||||
final community = widget.community;
|
||||
|
||||
_calculateAllCardWidths(community.spaces);
|
||||
|
||||
final levelXOffset = <int, double>{};
|
||||
_calculateLayout(community.spaces, 0, levelXOffset);
|
||||
|
||||
@ -240,6 +276,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
painter: SpacesConnectionsArrowPainter(
|
||||
connections: connections,
|
||||
positions: _positions,
|
||||
cardWidths: _cardWidths,
|
||||
highlightedUuids: highlightedUuids,
|
||||
),
|
||||
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
||||
@ -271,6 +308,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
continue;
|
||||
}
|
||||
|
||||
final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth;
|
||||
final isHighlighted = highlightedUuids.contains(space.uuid);
|
||||
final hasNoSelectedSpace = widget.selectedSpace == null;
|
||||
|
||||
@ -278,14 +316,10 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
buildSpaceContainer: () {
|
||||
return Opacity(
|
||||
opacity: hasNoSelectedSpace || isHighlighted ? 1.0 : 0.5,
|
||||
child: Tooltip(
|
||||
message: space.spaceName,
|
||||
preferBelow: false,
|
||||
child: SpaceCell(
|
||||
onTap: () => _onSpaceTapped(space),
|
||||
icon: space.icon,
|
||||
name: space.spaceName,
|
||||
),
|
||||
child: SpaceCell(
|
||||
onTap: () => _onSpaceTapped(space),
|
||||
icon: space.icon,
|
||||
name: space.spaceName,
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -305,7 +339,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
Positioned(
|
||||
left: position.dx,
|
||||
top: position.dy,
|
||||
width: _cardWidth,
|
||||
width: cardWidth,
|
||||
height: _cardHeight,
|
||||
child: Draggable<SpaceReorderDataModel>(
|
||||
data: reorderData,
|
||||
@ -314,7 +348,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
child: Opacity(
|
||||
opacity: 0.2,
|
||||
child: SizedBox(
|
||||
width: _cardWidth,
|
||||
width: cardWidth,
|
||||
height: _cardHeight,
|
||||
child: spaceCard,
|
||||
),
|
||||
@ -330,7 +364,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||
);
|
||||
|
||||
final targetPos = Offset(
|
||||
position.dx + _cardWidth + (_horizontalSpacing / 4) - 20,
|
||||
position.dx + cardWidth + (_horizontalSpacing / 4) - 20,
|
||||
position.dy,
|
||||
);
|
||||
widgets.add(_buildDropTarget(parent, community, i + 1, targetPos));
|
||||
|
@ -20,21 +20,19 @@ class SpaceCell extends StatelessWidget {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 150,
|
||||
padding: const EdgeInsetsDirectional.only(end: 10),
|
||||
height: 70,
|
||||
decoration: _containerDecoration(),
|
||||
child: Row(
|
||||
spacing: 10,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildIconContainer(),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
name,
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
Text(
|
||||
name,
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -74,8 +72,9 @@ class SpaceCell extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: ColorsManager.lightGrayColor.withValues(alpha: 0.25),
|
||||
blurRadius: 7,
|
||||
color: ColorsManager.lightGrayColor.withValues(alpha: 0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
|
@ -5,6 +5,8 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog_form.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_loading_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_status_widget.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
@ -45,8 +47,8 @@ class DeleteSpaceDialog extends StatelessWidget {
|
||||
communityUuid: community.uuid,
|
||||
),
|
||||
DeleteSpaceLoading() => const DeleteSpaceLoadingWidget(),
|
||||
DeleteSpaceSuccess(:final successMessage) => DeleteSpaceStatusWidget(
|
||||
message: successMessage,
|
||||
DeleteSpaceSuccess() => DeleteSpaceStatusWidget(
|
||||
message: state.successMessage,
|
||||
icon: const Icon(
|
||||
Icons.check_circle,
|
||||
size: 92,
|
||||
@ -69,50 +71,3 @@ class DeleteSpaceDialog extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DeleteSpaceLoadingWidget extends StatelessWidget {
|
||||
const DeleteSpaceLoadingWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SizedBox.square(
|
||||
dimension: 32,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DeleteSpaceStatusWidget extends StatelessWidget {
|
||||
const DeleteSpaceStatusWidget({
|
||||
required this.message,
|
||||
required this.icon,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String message;
|
||||
final Widget icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
icon,
|
||||
SelectableText(
|
||||
message,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 22,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
child: const Text('Close'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DeleteSpaceLoadingWidget extends StatelessWidget {
|
||||
const DeleteSpaceLoadingWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SizedBox.square(
|
||||
dimension: 32,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class DeleteSpaceStatusWidget extends StatelessWidget {
|
||||
const DeleteSpaceStatusWidget({
|
||||
required this.message,
|
||||
required this.icon,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String message;
|
||||
final Widget icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
icon,
|
||||
SelectableText(
|
||||
message,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 22,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
child: const Text('Close'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user