mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
fix add subspace bugs and plusButton widget
This commit is contained in:
@ -67,7 +67,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
connections =
|
||||
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
_adjustCanvasSizeForSpaces();
|
||||
_nameController = TextEditingController(
|
||||
text: widget.selectedCommunity?.name ?? '',
|
||||
@ -96,13 +97,15 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
if (oldWidget.spaces != widget.spaces) {
|
||||
setState(() {
|
||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
connections =
|
||||
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
_adjustCanvasSizeForSpaces();
|
||||
realignTree();
|
||||
});
|
||||
}
|
||||
|
||||
if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) {
|
||||
if (widget.selectedSpace != oldWidget.selectedSpace &&
|
||||
widget.selectedSpace != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_moveToSpace(widget.selectedSpace!);
|
||||
});
|
||||
@ -185,7 +188,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
connection, widget.selectedSpace)
|
||||
? 1.0
|
||||
: 0.3, // Adjust opacity
|
||||
child: CustomPaint(painter: CurvedLinePainter([connection])),
|
||||
child: CustomPaint(
|
||||
painter: CurvedLinePainter([connection])),
|
||||
),
|
||||
for (var entry in spaces.asMap().entries)
|
||||
if (entry.value.status != SpaceStatus.deleted &&
|
||||
@ -195,9 +199,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
top: entry.value.position.dy,
|
||||
child: SpaceCardWidget(
|
||||
index: entry.key,
|
||||
onButtonTap: (int index, Offset newPosition, String direction) {
|
||||
onButtonTap: (int index, Offset newPosition,
|
||||
String direction) {
|
||||
_showCreateSpaceDialog(screenSize,
|
||||
position: spaces[index].position + newPosition,
|
||||
position:
|
||||
spaces[index].position + newPosition,
|
||||
parentIndex: index,
|
||||
direction: direction,
|
||||
projectTags: widget.projectTags);
|
||||
@ -210,8 +216,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
_updateNodePosition(entry.value, newPosition);
|
||||
},
|
||||
buildSpaceContainer: (int index) {
|
||||
final bool isHighlighted = SpaceHelper.isHighlightedSpace(
|
||||
spaces[index], widget.selectedSpace);
|
||||
final bool isHighlighted =
|
||||
SpaceHelper.isHighlightedSpace(
|
||||
spaces[index], widget.selectedSpace);
|
||||
|
||||
return Opacity(
|
||||
opacity: isHighlighted ? 1.0 : 0.3,
|
||||
@ -299,19 +306,25 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
return CreateSpaceDialog(
|
||||
products: widget.products,
|
||||
spaceModels: widget.spaceModels,
|
||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
allTags:
|
||||
TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||
projectTags: projectTags,
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
||||
onCreateSpace: (String name,
|
||||
String icon,
|
||||
List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<Tag>? tags) {
|
||||
setState(() {
|
||||
// Set the first space in the center or use passed position
|
||||
Offset newPosition;
|
||||
if (parentIndex != null) {
|
||||
newPosition =
|
||||
getBalancedChildPosition(spaces[parentIndex]); // Ensure balanced position
|
||||
newPosition = getBalancedChildPosition(
|
||||
spaces[parentIndex]); // Ensure balanced position
|
||||
} else {
|
||||
newPosition = position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||
newPosition =
|
||||
position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||
}
|
||||
|
||||
SpaceModel newSpace = SpaceModel(
|
||||
@ -360,16 +373,21 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
name: widget.selectedSpace!.name,
|
||||
icon: widget.selectedSpace!.icon,
|
||||
projectTags: widget.projectTags,
|
||||
parentSpace:
|
||||
SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces),
|
||||
parentSpace: SpaceHelper.findSpaceByInternalId(
|
||||
widget.selectedSpace?.parent?.internalId, spaces),
|
||||
editSpace: widget.selectedSpace,
|
||||
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
||||
tags: widget.selectedSpace?.tags,
|
||||
subspaces: widget.selectedSpace?.subspaces,
|
||||
isEdit: true,
|
||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
||||
allTags: TagHelper.getAllTagValues(
|
||||
widget.communities, widget.spaceModels),
|
||||
onCreateSpace: (String name,
|
||||
String icon,
|
||||
List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<Tag>? tags) {
|
||||
setState(() {
|
||||
// Update the space's properties
|
||||
widget.selectedSpace!.name = name;
|
||||
@ -379,7 +397,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
widget.selectedSpace!.tags = tags;
|
||||
|
||||
if (widget.selectedSpace!.status != SpaceStatus.newSpace) {
|
||||
widget.selectedSpace!.status = SpaceStatus.modified; // Mark as modified
|
||||
widget.selectedSpace!.status =
|
||||
SpaceStatus.modified; // Mark as modified
|
||||
}
|
||||
|
||||
for (var space in spaces) {
|
||||
@ -410,7 +429,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
Map<String, SpaceModel> idToSpace = {};
|
||||
|
||||
void flatten(SpaceModel space) {
|
||||
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
||||
if (space.status == SpaceStatus.deleted ||
|
||||
space.status == SpaceStatus.parentDeleted) {
|
||||
return;
|
||||
}
|
||||
result.add(space);
|
||||
@ -532,13 +552,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
void _selectSpace(BuildContext context, SpaceModel space) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space),
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: space),
|
||||
);
|
||||
}
|
||||
|
||||
void _deselectSpace(BuildContext context) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||
);
|
||||
}
|
||||
|
||||
@ -708,7 +731,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent);
|
||||
|
||||
duplicated.position = Offset(space.position.dx + 300, space.position.dy + 100);
|
||||
duplicated.position =
|
||||
Offset(space.position.dx + 300, space.position.dy + 100);
|
||||
List<SpaceModel> duplicatedSubtree = [];
|
||||
void collectSubtree(SpaceModel node) {
|
||||
duplicatedSubtree.add(node);
|
||||
@ -739,7 +763,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
}
|
||||
|
||||
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||
final duplicatedName =
|
||||
SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||
|
||||
final newSpace = SpaceModel(
|
||||
name: duplicatedName,
|
||||
|
@ -82,8 +82,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
super.initState();
|
||||
selectedIcon = widget.icon ?? Assets.location;
|
||||
nameController = TextEditingController(text: widget.name ?? '');
|
||||
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
selectedProducts =
|
||||
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
isOkButtonEnabled =
|
||||
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
if (widget.currentSpaceModel != null) {
|
||||
subspaces = [];
|
||||
tags = [];
|
||||
@ -96,13 +98,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isSpaceModelDisabled =
|
||||
(tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty);
|
||||
bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty ||
|
||||
subspaces != null && subspaces!.isNotEmpty);
|
||||
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
||||
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
return AlertDialog(
|
||||
title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'),
|
||||
title: widget.isEdit
|
||||
? const Text('Edit Space')
|
||||
: const Text('Create New Space'),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SizedBox(
|
||||
width: screenWidth * 0.5,
|
||||
@ -176,8 +180,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
|
||||
if (!isNameFieldInvalid) {
|
||||
if (SpaceHelper.isNameConflict(
|
||||
value, widget.parentSpace, widget.editSpace)) {
|
||||
if (SpaceHelper.isNameConflict(value,
|
||||
widget.parentSpace, widget.editSpace)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
@ -244,7 +248,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
onPressed: () {
|
||||
isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context);
|
||||
isSpaceModelDisabled
|
||||
? null
|
||||
: _showLinkSpaceModelDialog(context);
|
||||
},
|
||||
child: ButtonContentWidget(
|
||||
svgAssets: Assets.link,
|
||||
@ -254,7 +260,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
)
|
||||
: Container(
|
||||
width: screenWidth * 0.25,
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10.0, horizontal: 16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
@ -269,7 +276,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(color: ColorsManager.spaceColor),
|
||||
.copyWith(
|
||||
color: ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
@ -340,12 +348,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
onPressed: () async {
|
||||
isTagsAndSubspaceModelDisabled
|
||||
? null
|
||||
: _showSubSpaceDialog(
|
||||
context, enteredName, [], false, widget.products, subspaces);
|
||||
: _showSubSpaceDialog(context, enteredName,
|
||||
[], false, widget.products, subspaces);
|
||||
},
|
||||
child: ButtonContentWidget(
|
||||
icon: Icons.add,
|
||||
label: 'Create Sub Space',
|
||||
label: 'Create Sub Spaces',
|
||||
disabled: isTagsAndSubspaceModelDisabled,
|
||||
),
|
||||
)
|
||||
@ -368,16 +376,22 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
if (subspaces != null)
|
||||
...subspaces!.map((subspace) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
SubspaceNameDisplayWidget(
|
||||
text: subspace.subspaceName,
|
||||
validateName: (updatedName) {
|
||||
bool nameExists = subspaces!.any((s) {
|
||||
bool isSameId = s.internalId == subspace.internalId;
|
||||
bool isSameName =
|
||||
s.subspaceName.trim().toLowerCase() ==
|
||||
updatedName.trim().toLowerCase();
|
||||
bool nameExists =
|
||||
subspaces!.any((s) {
|
||||
bool isSameId = s.internalId ==
|
||||
subspace.internalId;
|
||||
bool isSameName = s.subspaceName
|
||||
.trim()
|
||||
.toLowerCase() ==
|
||||
updatedName
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
return !isSameId && isSameName;
|
||||
});
|
||||
@ -386,7 +400,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
},
|
||||
onNameChanged: (updatedName) {
|
||||
setState(() {
|
||||
subspace.subspaceName = updatedName;
|
||||
subspace.subspaceName =
|
||||
updatedName;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -395,8 +410,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
}),
|
||||
EditChip(
|
||||
onTap: () async {
|
||||
_showSubSpaceDialog(context, enteredName, [], true,
|
||||
widget.products, subspaces);
|
||||
_showSubSpaceDialog(context, enteredName,
|
||||
[], true, widget.products, subspaces);
|
||||
},
|
||||
)
|
||||
],
|
||||
@ -405,7 +420,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
(tags?.isNotEmpty == true ||
|
||||
subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true)
|
||||
subspaces?.any((subspace) =>
|
||||
subspace.tags?.isNotEmpty == true) ==
|
||||
true)
|
||||
? SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
child: Container(
|
||||
@ -425,14 +442,16 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
// Combine tags from spaceModel and subspaces
|
||||
...TagHelper.groupTags([
|
||||
...?tags,
|
||||
...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
||||
...?subspaces?.expand(
|
||||
(subspace) => subspace.tags ?? [])
|
||||
]).entries.map(
|
||||
(entry) => Chip(
|
||||
avatar: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: SvgPicture.asset(
|
||||
entry.key.icon ?? 'assets/icons/gateway.svg',
|
||||
entry.key.icon ??
|
||||
'assets/icons/gateway.svg',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
@ -441,11 +460,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.spaceColor),
|
||||
?.copyWith(
|
||||
color: ColorsManager
|
||||
.spaceColor),
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
backgroundColor:
|
||||
ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius:
|
||||
BorderRadius.circular(16),
|
||||
side: const BorderSide(
|
||||
color: ColorsManager.spaceColor,
|
||||
),
|
||||
@ -460,15 +483,18 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
products: widget.products,
|
||||
subspaces: subspaces,
|
||||
allTags: widget.allTags,
|
||||
addedProducts:
|
||||
TagHelper.createInitialSelectedProductsForTags(
|
||||
addedProducts: TagHelper
|
||||
.createInitialSelectedProductsForTags(
|
||||
tags ?? [], subspaces),
|
||||
title: 'Edit Device',
|
||||
initialTags: TagHelper.generateInitialForTags(
|
||||
spaceTags: tags, subspaces: subspaces),
|
||||
initialTags:
|
||||
TagHelper.generateInitialForTags(
|
||||
spaceTags: tags,
|
||||
subspaces: subspaces),
|
||||
spaceName: widget.name ?? '',
|
||||
projectTags: widget.projectTags,
|
||||
onSave: (updatedTags, updatedSubspaces) {
|
||||
onSave:
|
||||
(updatedTags, updatedSubspaces) {
|
||||
setState(() {
|
||||
tags = updatedTags;
|
||||
subspaces = updatedSubspaces;
|
||||
@ -529,17 +555,25 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
} else if (isNameFieldExist) {
|
||||
return;
|
||||
} else {
|
||||
String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
|
||||
String newName = enteredName.isNotEmpty
|
||||
? enteredName
|
||||
: (widget.name ?? '');
|
||||
if (newName.isNotEmpty) {
|
||||
widget.onCreateSpace(newName, selectedIcon, selectedProducts,
|
||||
selectedSpaceModel, subspaces, tags);
|
||||
widget.onCreateSpace(
|
||||
newName,
|
||||
selectedIcon,
|
||||
selectedProducts,
|
||||
selectedSpaceModel,
|
||||
subspaces,
|
||||
tags);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
borderRadius: 10,
|
||||
backgroundColor:
|
||||
isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor,
|
||||
backgroundColor: isOkButtonEnabled
|
||||
? ColorsManager.secondaryColor
|
||||
: ColorsManager.grayColor,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
),
|
||||
@ -586,24 +620,31 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showSubSpaceDialog(BuildContext context, String name, final List<Tag>? spaceTags,
|
||||
bool isEdit, List<ProductModel>? products, final List<SubspaceModel>? existingSubSpaces) {
|
||||
void _showSubSpaceDialog(
|
||||
BuildContext context,
|
||||
String name,
|
||||
final List<Tag>? spaceTags,
|
||||
bool isEdit,
|
||||
List<ProductModel>? products,
|
||||
final List<SubspaceModel>? existingSubSpaces) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CreateSubSpaceDialog(
|
||||
spaceName: name,
|
||||
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
||||
dialogTitle: isEdit ? 'Edit Sub-spaces' : 'Create Sub-spaces',
|
||||
products: products,
|
||||
existingSubSpaces: existingSubSpaces,
|
||||
onSave: (slectedSubspaces) {
|
||||
final List<Tag> tagsToAppendToSpace = [];
|
||||
|
||||
if (slectedSubspaces != null) {
|
||||
final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet();
|
||||
if (slectedSubspaces != null && slectedSubspaces.isNotEmpty) {
|
||||
final updatedIds =
|
||||
slectedSubspaces.map((s) => s.internalId).toSet();
|
||||
if (existingSubSpaces != null) {
|
||||
final deletedSubspaces =
|
||||
existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList();
|
||||
final deletedSubspaces = existingSubSpaces
|
||||
.where((s) => !updatedIds.contains(s.internalId))
|
||||
.toList();
|
||||
for (var s in deletedSubspaces) {
|
||||
if (s.tags != null) {
|
||||
tagsToAppendToSpace.addAll(s.tags!);
|
||||
@ -623,15 +664,16 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showTagCreateDialog(
|
||||
BuildContext context, String name, bool isEdit, List<ProductModel>? products) {
|
||||
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||
List<ProductModel>? products) {
|
||||
isEdit
|
||||
? showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AssignTagDialog(
|
||||
title: 'Edit Device',
|
||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(
|
||||
tags, subspaces),
|
||||
spaceName: name,
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
@ -646,7 +688,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
if (subspaces != null) {
|
||||
for (final subspace in subspaces!) {
|
||||
for (final selectedSubspace in selectedSubspaces) {
|
||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
||||
if (subspace.subspaceName ==
|
||||
selectedSubspace.subspaceName) {
|
||||
subspace.tags = selectedSubspace.tags;
|
||||
}
|
||||
}
|
||||
@ -670,7 +713,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
allTags: widget.allTags,
|
||||
projectTags: widget.projectTags,
|
||||
initialSelectedProducts:
|
||||
TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
||||
TagHelper.createInitialSelectedProductsForTags(
|
||||
tags, subspaces),
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||
setState(() {
|
||||
tags = selectedSpaceTags;
|
||||
@ -680,7 +724,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
if (subspaces != null) {
|
||||
for (final subspace in subspaces!) {
|
||||
for (final selectedSubspace in selectedSubspaces) {
|
||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
||||
if (subspace.subspaceName ==
|
||||
selectedSubspace.subspaceName) {
|
||||
subspace.tags = selectedSubspace.tags;
|
||||
}
|
||||
}
|
||||
|
@ -17,36 +17,33 @@ class PlusButtonWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Positioned(
|
||||
left: offset.dx,
|
||||
top: offset.dy,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Offset newPosition;
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
newPosition = const Offset(-200, 0);
|
||||
break;
|
||||
case 'right':
|
||||
newPosition = const Offset(200, 0);
|
||||
break;
|
||||
case 'down':
|
||||
newPosition = const Offset(0, 150);
|
||||
break;
|
||||
default:
|
||||
newPosition = Offset.zero;
|
||||
}
|
||||
onButtonTap(index, newPosition, direction);
|
||||
},
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.spaceColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20),
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Offset newPosition;
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
newPosition = const Offset(-200, 0);
|
||||
break;
|
||||
case 'right':
|
||||
newPosition = const Offset(200, 0);
|
||||
break;
|
||||
case 'down':
|
||||
newPosition = const Offset(0, 150);
|
||||
break;
|
||||
default:
|
||||
newPosition = Offset.zero;
|
||||
}
|
||||
onButtonTap(index, newPosition, direction);
|
||||
},
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.spaceColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child:
|
||||
const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -25,35 +25,34 @@ class SpaceCardWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanUpdate: (details) {
|
||||
// Call the provided callback to update the position
|
||||
final newPosition = position + details.delta;
|
||||
onPositionChanged(newPosition);
|
||||
},
|
||||
child: MouseRegion(
|
||||
onEnter: (_) {
|
||||
// Call the provided callback to handle hover state
|
||||
onHoverChanged(index, true);
|
||||
},
|
||||
onExit: (_) {
|
||||
// Call the provided callback to handle hover state
|
||||
onHoverChanged(index, false);
|
||||
},
|
||||
return MouseRegion(
|
||||
onEnter: (_) => onHoverChanged(index, true),
|
||||
onExit: (_) => onHoverChanged(index, false),
|
||||
child: SizedBox(
|
||||
width: 140, // Make sure this covers both card and plus button
|
||||
height: 90,
|
||||
child: Stack(
|
||||
clipBehavior: Clip
|
||||
.none, // Allow hovering elements to be displayed outside the boundary
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
buildSpaceContainer(index), // Build the space container
|
||||
if (isHovered) ...[
|
||||
PlusButtonWidget(
|
||||
index: index,
|
||||
direction: 'down',
|
||||
offset: const Offset(63, 50),
|
||||
onButtonTap: onButtonTap,
|
||||
// Main card
|
||||
Container(
|
||||
width: 140,
|
||||
height: 80,
|
||||
alignment: Alignment.center,
|
||||
color: Colors.transparent,
|
||||
child: buildSpaceContainer(index),
|
||||
),
|
||||
// Plus button (NO inner Positioned!)
|
||||
if (isHovered)
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: PlusButtonWidget(
|
||||
index: index,
|
||||
direction: 'down',
|
||||
offset: Offset.zero,
|
||||
onButtonTap: onButtonTap,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -79,6 +79,22 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'press Enter to Save',
|
||||
style: context.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
const Icon(Icons.save_as_sharp, size: 10),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
width: context.screenWidth * 0.35,
|
||||
@ -101,13 +117,15 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||
final index = entry.key;
|
||||
final subSpace = entry.value;
|
||||
|
||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||
final lowerName =
|
||||
subSpace.subspaceName.toLowerCase();
|
||||
|
||||
final duplicateIndices = state.subSpaces
|
||||
.asMap()
|
||||
.entries
|
||||
.where((e) =>
|
||||
e.value.subspaceName.toLowerCase() == lowerName)
|
||||
e.value.subspaceName.toLowerCase() ==
|
||||
lowerName)
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
final isDuplicate = duplicateIndices.length > 1 &&
|
||||
@ -182,10 +200,32 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
onPressed: state.errorMessage.isEmpty
|
||||
? () {
|
||||
final subSpacesBloc = context.read<SubSpaceBloc>();
|
||||
final subSpaces = subSpacesBloc.state.subSpaces;
|
||||
? () async {
|
||||
final trimmedValue =
|
||||
_subspaceNameController.text.trim();
|
||||
|
||||
final subSpacesBloc =
|
||||
context.read<SubSpaceBloc>();
|
||||
if (trimmedValue.isNotEmpty) {
|
||||
subSpacesBloc.add(
|
||||
AddSubSpace(
|
||||
SubspaceModel(
|
||||
subspaceName: trimmedValue,
|
||||
disabled: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
_subspaceNameController.clear();
|
||||
}
|
||||
|
||||
await Future.delayed(
|
||||
const Duration(milliseconds: 10));
|
||||
final subSpaces =
|
||||
subSpacesBloc.state.subSpaces;
|
||||
// if (subSpaces.isNotEmpty) {
|
||||
widget.onSave?.call(subSpaces);
|
||||
// }
|
||||
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
: null,
|
||||
|
Reference in New Issue
Block a user