mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Feat: rename stack inline (#3781)
* feat: rename stack in-line * feat: rename stack in-line * chore: compiler issues * fix: conflicts and cleaning * fix: code lost after merge * test: fix failing rust tests * fix: tauri localization wrong keys --------- Co-authored-by: Richard Shiue <71320345+richardshiue@users.noreply.github.com>
This commit is contained in:
@ -5,6 +5,7 @@ import 'package:dartz/dartz.dart';
|
||||
|
||||
class GroupBackendService {
|
||||
final String viewId;
|
||||
|
||||
GroupBackendService(this.viewId);
|
||||
|
||||
Future<Either<Unit, FlowyError>> groupByField({
|
||||
@ -19,10 +20,15 @@ class GroupBackendService {
|
||||
|
||||
Future<Either<Unit, FlowyError>> updateGroup({
|
||||
required String groupId,
|
||||
required String fieldId,
|
||||
String? name,
|
||||
bool? visible,
|
||||
}) {
|
||||
final payload = UpdateGroupPB.create()..groupId = groupId;
|
||||
final payload = UpdateGroupPB.create()
|
||||
..fieldId = fieldId
|
||||
..viewId = viewId
|
||||
..groupId = groupId;
|
||||
|
||||
if (name != null) {
|
||||
payload.name = name;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'dart:collection';
|
||||
|
||||
import 'package:appflowy/plugins/database_view/application/defines.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/group/group_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@ -11,6 +12,7 @@ import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@ -22,6 +24,7 @@ import 'group_controller.dart';
|
||||
part 'board_bloc.freezed.dart';
|
||||
|
||||
class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
late final GroupBackendService groupBackendSvc;
|
||||
final DatabaseController databaseController;
|
||||
late final AppFlowyBoardController boardController;
|
||||
final LinkedHashMap<String, GroupController> groupControllers =
|
||||
@ -34,23 +37,15 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
required ViewPB view,
|
||||
required this.databaseController,
|
||||
}) : super(BoardState.initial(view.id)) {
|
||||
groupBackendSvc = GroupBackendService(viewId);
|
||||
boardController = AppFlowyBoardController(
|
||||
onMoveGroup: (
|
||||
fromGroupId,
|
||||
fromIndex,
|
||||
toGroupId,
|
||||
toIndex,
|
||||
) {
|
||||
onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
|
||||
databaseController.moveGroup(
|
||||
fromGroupId: fromGroupId,
|
||||
toGroupId: toGroupId,
|
||||
);
|
||||
},
|
||||
onMoveGroupItem: (
|
||||
groupId,
|
||||
fromIndex,
|
||||
toIndex,
|
||||
) {
|
||||
onMoveGroupItem: (groupId, fromIndex, toIndex) {
|
||||
final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex);
|
||||
final toRow = groupControllers[groupId]?.rowAtIndex(toIndex);
|
||||
if (fromRow != null) {
|
||||
@ -61,12 +56,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
);
|
||||
}
|
||||
},
|
||||
onMoveGroupItemToGroup: (
|
||||
fromGroupId,
|
||||
fromIndex,
|
||||
toGroupId,
|
||||
toIndex,
|
||||
) {
|
||||
onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
|
||||
final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex);
|
||||
final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex);
|
||||
if (fromRow != null) {
|
||||
@ -107,12 +97,11 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
didCreateRow: (group, row, int? index) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
editingRow: Some(
|
||||
BoardEditingRow(
|
||||
group: group,
|
||||
row: row,
|
||||
index: index,
|
||||
),
|
||||
isEditingRow: true,
|
||||
editingRow: BoardEditingRow(
|
||||
group: group,
|
||||
row: row,
|
||||
index: index,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -121,23 +110,26 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
startEditingRow: (group, row) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
editingRow: Some(
|
||||
BoardEditingRow(
|
||||
group: group,
|
||||
row: row,
|
||||
index: null,
|
||||
),
|
||||
editingRow: BoardEditingRow(
|
||||
group: group,
|
||||
row: row,
|
||||
index: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
_groupItemStartEditing(group, row, true);
|
||||
},
|
||||
endEditingRow: (rowId) {
|
||||
state.editingRow.fold(() => null, (editingRow) {
|
||||
assert(editingRow.row.id == rowId);
|
||||
_groupItemStartEditing(editingRow.group, editingRow.row, false);
|
||||
emit(state.copyWith(editingRow: none()));
|
||||
});
|
||||
if (state.editingRow != null && state.isEditingRow) {
|
||||
assert(state.editingRow!.row.id == rowId);
|
||||
_groupItemStartEditing(
|
||||
state.editingRow!.group,
|
||||
state.editingRow!.row,
|
||||
false,
|
||||
);
|
||||
|
||||
emit(state.copyWith(isEditingRow: false));
|
||||
}
|
||||
},
|
||||
didReceiveGridUpdate: (DatabasePB grid) {
|
||||
emit(state.copyWith(grid: Some(grid)));
|
||||
@ -152,6 +144,20 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
),
|
||||
);
|
||||
},
|
||||
startEditingHeader: (String groupId) {
|
||||
emit(
|
||||
state.copyWith(isEditingHeader: true, editingHeaderId: groupId),
|
||||
);
|
||||
},
|
||||
endEditingHeader: (String groupId, String groupName) async {
|
||||
await groupBackendSvc.updateGroup(
|
||||
fieldId: groupControllers.values.first.group.fieldId,
|
||||
groupId: groupId,
|
||||
name: groupName,
|
||||
);
|
||||
|
||||
emit(state.copyWith(isEditingHeader: false));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -303,6 +309,10 @@ class BoardEvent with _$BoardEvent {
|
||||
const factory BoardEvent.initial() = _InitialBoard;
|
||||
const factory BoardEvent.createBottomRow(String groupId) = _CreateBottomRow;
|
||||
const factory BoardEvent.createHeaderRow(String groupId) = _CreateHeaderRow;
|
||||
const factory BoardEvent.startEditingHeader(String groupId) =
|
||||
_StartEditingHeader;
|
||||
const factory BoardEvent.endEditingHeader(String groupId, String groupName) =
|
||||
_EndEditingHeader;
|
||||
const factory BoardEvent.didCreateRow(
|
||||
GroupPB group,
|
||||
RowMetaPB row,
|
||||
@ -327,7 +337,10 @@ class BoardState with _$BoardState {
|
||||
required String viewId,
|
||||
required Option<DatabasePB> grid,
|
||||
required List<String> groupIds,
|
||||
required Option<BoardEditingRow> editingRow,
|
||||
required bool isEditingHeader,
|
||||
String? editingHeaderId,
|
||||
required bool isEditingRow,
|
||||
BoardEditingRow? editingRow,
|
||||
required LoadingState loadingState,
|
||||
required Option<FlowyError> noneOrError,
|
||||
}) = _BoardState;
|
||||
@ -336,7 +349,8 @@ class BoardState with _$BoardState {
|
||||
grid: none(),
|
||||
viewId: viewId,
|
||||
groupIds: [],
|
||||
editingRow: none(),
|
||||
isEditingHeader: false,
|
||||
isEditingRow: false,
|
||||
noneOrError: none(),
|
||||
loadingState: const LoadingState.loading(),
|
||||
);
|
||||
|
@ -8,11 +8,11 @@ import 'package:appflowy/plugins/database_view/application/database_controller.d
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/presentation/widgets/board_column_header.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@ -82,7 +82,7 @@ class BoardPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
return BlocProvider<BoardBloc>(
|
||||
create: (context) => BoardBloc(
|
||||
view: view,
|
||||
databaseController: databaseController,
|
||||
@ -133,18 +133,20 @@ class _BoardContentState extends State<BoardContent> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
scrollManager = AppFlowyBoardScrollController();
|
||||
renderHook.addSelectOptionHook((options, groupId, _) {
|
||||
// The cell should hide if the option id is equal to the groupId.
|
||||
final isInGroup =
|
||||
options.where((element) => element.id == groupId).isNotEmpty;
|
||||
|
||||
if (isInGroup || options.isEmpty) {
|
||||
return const SizedBox();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -155,92 +157,51 @@ class _BoardContentState extends State<BoardContent> {
|
||||
widget.onEditStateChanged?.call();
|
||||
},
|
||||
child: BlocBuilder<BoardBloc, BoardState>(
|
||||
// Only rebuild when groups are added/removed/rearranged
|
||||
buildWhen: (previous, current) => previous.groupIds != current.groupIds,
|
||||
builder: (context, state) {
|
||||
return Padding(
|
||||
padding: GridSize.contentInsets,
|
||||
child: _buildBoard(context),
|
||||
child: AppFlowyBoard(
|
||||
boardScrollController: scrollManager,
|
||||
scrollController: ScrollController(),
|
||||
controller: context.read<BoardBloc>().boardController,
|
||||
headerBuilder: (_, groupData) => BlocProvider<BoardBloc>.value(
|
||||
value: context.read<BoardBloc>(),
|
||||
child: BoardColumnHeader(
|
||||
groupData: groupData,
|
||||
margin: config.headerPadding,
|
||||
),
|
||||
),
|
||||
footerBuilder: _buildFooter,
|
||||
cardBuilder: (_, column, columnItem) => _buildCard(
|
||||
context,
|
||||
column,
|
||||
columnItem,
|
||||
),
|
||||
groupConstraints: const BoxConstraints.tightFor(width: 300),
|
||||
config: AppFlowyBoardConfig(
|
||||
groupBackgroundColor:
|
||||
Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBoard(BuildContext context) {
|
||||
return AppFlowyBoard(
|
||||
boardScrollController: scrollManager,
|
||||
scrollController: ScrollController(),
|
||||
controller: context.read<BoardBloc>().boardController,
|
||||
headerBuilder: _buildHeader,
|
||||
footerBuilder: _buildFooter,
|
||||
cardBuilder: (_, column, columnItem) => _buildCard(
|
||||
context,
|
||||
column,
|
||||
columnItem,
|
||||
),
|
||||
groupConstraints: const BoxConstraints.tightFor(width: 300),
|
||||
config: AppFlowyBoardConfig(
|
||||
groupBackgroundColor: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleEditStateChanged(BoardState state, BuildContext context) {
|
||||
state.editingRow.fold(
|
||||
() => null,
|
||||
(editingRow) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (editingRow.index != null) {
|
||||
} else {
|
||||
scrollManager.scrollToBottom(editingRow.group.groupId);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildHeader(
|
||||
BuildContext context,
|
||||
AppFlowyGroupData groupData,
|
||||
) {
|
||||
final boardCustomData = groupData.customData as GroupData;
|
||||
return AppFlowyGroupHeader(
|
||||
title: Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: FlowyText.medium(
|
||||
groupData.headerData.groupName,
|
||||
fontSize: 14,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
icon: _buildHeaderIcon(boardCustomData),
|
||||
addIcon: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: FlowySvg(
|
||||
FlowySvgs.add_s,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
),
|
||||
),
|
||||
onAddButtonClick: () {
|
||||
context.read<BoardBloc>().add(
|
||||
BoardEvent.createHeaderRow(groupData.id),
|
||||
);
|
||||
},
|
||||
height: 50,
|
||||
margin: config.headerPadding,
|
||||
);
|
||||
if (state.isEditingRow && state.editingRow != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (state.editingRow!.index == null) {
|
||||
scrollManager.scrollToBottom(state.editingRow!.group.groupId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildFooter(BuildContext context, AppFlowyGroupData columnData) {
|
||||
// final boardCustomData = columnData.customData as BoardCustomData;
|
||||
// final group = boardCustomData.group;
|
||||
|
||||
return AppFlowyGroupFooter(
|
||||
icon: SizedBox(
|
||||
height: 20,
|
||||
@ -251,7 +212,7 @@ class _BoardContentState extends State<BoardContent> {
|
||||
),
|
||||
),
|
||||
title: FlowyText.medium(
|
||||
LocaleKeys.board_column_create_new_card.tr(),
|
||||
LocaleKeys.board_column_createNewCard.tr(),
|
||||
fontSize: 14,
|
||||
),
|
||||
height: 50,
|
||||
@ -269,25 +230,21 @@ class _BoardContentState extends State<BoardContent> {
|
||||
AppFlowyGroupData afGroupData,
|
||||
AppFlowyGroupItem afGroupItem,
|
||||
) {
|
||||
final boardBloc = context.read<BoardBloc>();
|
||||
final groupItem = afGroupItem as GroupItem;
|
||||
final groupData = afGroupData.customData as GroupData;
|
||||
final rowMeta = groupItem.row;
|
||||
final rowCache = context.read<BoardBloc>().getRowCache();
|
||||
final rowCache = boardBloc.getRowCache();
|
||||
|
||||
/// Return placeholder widget if the rowCache is null.
|
||||
if (rowCache == null) return SizedBox(key: ObjectKey(groupItem));
|
||||
if (rowCache == null) return SizedBox.shrink(key: ObjectKey(groupItem));
|
||||
final cellCache = rowCache.cellCache;
|
||||
final fieldController = context.read<BoardBloc>().fieldController;
|
||||
final viewId = context.read<BoardBloc>().viewId;
|
||||
final fieldController = boardBloc.fieldController;
|
||||
final viewId = boardBloc.viewId;
|
||||
|
||||
final cellBuilder = CardCellBuilder<String>(cellCache);
|
||||
bool isEditing = false;
|
||||
context.read<BoardBloc>().state.editingRow.fold(
|
||||
() => null,
|
||||
(editingRow) {
|
||||
isEditing = editingRow.row.id == groupItem.row.id;
|
||||
},
|
||||
);
|
||||
final isEditing = boardBloc.state.isEditingRow &&
|
||||
boardBloc.state.editingRow?.row.id == groupItem.row.id;
|
||||
|
||||
final groupItemId = groupItem.row.id + groupData.group.groupId;
|
||||
return AppFlowyGroupCard(
|
||||
@ -305,26 +262,17 @@ class _BoardContentState extends State<BoardContent> {
|
||||
cellBuilder: cellBuilder,
|
||||
renderHook: renderHook,
|
||||
openCard: (context) => _openCard(
|
||||
viewId,
|
||||
groupData.group.groupId,
|
||||
fieldController,
|
||||
rowMeta,
|
||||
rowCache,
|
||||
context,
|
||||
context: context,
|
||||
viewId: viewId,
|
||||
groupId: groupData.group.groupId,
|
||||
fieldController: fieldController,
|
||||
rowMeta: rowMeta,
|
||||
rowCache: rowCache,
|
||||
),
|
||||
onStartEditing: () {
|
||||
context.read<BoardBloc>().add(
|
||||
BoardEvent.startEditingRow(
|
||||
groupData.group,
|
||||
groupItem.row,
|
||||
),
|
||||
);
|
||||
},
|
||||
onEndEditing: () {
|
||||
context
|
||||
.read<BoardBloc>()
|
||||
.add(BoardEvent.endEditingRow(groupItem.row.id));
|
||||
},
|
||||
onStartEditing: () => boardBloc
|
||||
.add(BoardEvent.startEditingRow(groupData.group, groupItem.row)),
|
||||
onEndEditing: () =>
|
||||
boardBloc.add(BoardEvent.endEditingRow(groupItem.row.id)),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -342,19 +290,19 @@ class _BoardContentState extends State<BoardContent> {
|
||||
);
|
||||
}
|
||||
|
||||
void _openCard(
|
||||
String viewId,
|
||||
String groupId,
|
||||
FieldController fieldController,
|
||||
RowMetaPB rowMetaPB,
|
||||
RowCache rowCache,
|
||||
BuildContext context,
|
||||
) {
|
||||
void _openCard({
|
||||
required BuildContext context,
|
||||
required String viewId,
|
||||
required String groupId,
|
||||
required FieldController fieldController,
|
||||
required RowMetaPB rowMeta,
|
||||
required RowCache rowCache,
|
||||
}) {
|
||||
final rowInfo = RowInfo(
|
||||
viewId: viewId,
|
||||
fields: UnmodifiableListView(fieldController.fieldInfos),
|
||||
rowMeta: rowMetaPB,
|
||||
rowId: rowMetaPB.id,
|
||||
rowMeta: rowMeta,
|
||||
rowId: rowMeta.id,
|
||||
);
|
||||
|
||||
final dataController = RowController(
|
||||
@ -375,41 +323,3 @@ class _BoardContentState extends State<BoardContent> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget? _buildHeaderIcon(GroupData customData) {
|
||||
Widget? widget;
|
||||
switch (customData.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
final group = customData.asCheckboxGroup()!;
|
||||
widget = FlowySvg(
|
||||
group.isCheck ? FlowySvgs.check_filled_s : FlowySvgs.uncheck_s,
|
||||
blendMode: BlendMode.dst,
|
||||
);
|
||||
break;
|
||||
case FieldType.DateTime:
|
||||
case FieldType.LastEditedTime:
|
||||
case FieldType.CreatedTime:
|
||||
break;
|
||||
case FieldType.MultiSelect:
|
||||
break;
|
||||
case FieldType.Number:
|
||||
break;
|
||||
case FieldType.RichText:
|
||||
break;
|
||||
case FieldType.SingleSelect:
|
||||
break;
|
||||
case FieldType.URL:
|
||||
break;
|
||||
case FieldType.Checklist:
|
||||
break;
|
||||
}
|
||||
|
||||
if (widget != null) {
|
||||
widget = SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: widget,
|
||||
);
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
|
@ -0,0 +1,228 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/application/board_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/card/define.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class BoardColumnHeader extends StatefulWidget {
|
||||
const BoardColumnHeader({
|
||||
super.key,
|
||||
required this.groupData,
|
||||
this.margin,
|
||||
});
|
||||
|
||||
final AppFlowyGroupData groupData;
|
||||
final EdgeInsets? margin;
|
||||
|
||||
@override
|
||||
State<BoardColumnHeader> createState() => _BoardColumnHeaderState();
|
||||
}
|
||||
|
||||
class _BoardColumnHeaderState extends State<BoardColumnHeader> {
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
|
||||
late final TextEditingController _controller =
|
||||
TextEditingController.fromValue(
|
||||
TextEditingValue(
|
||||
selection: TextSelection.collapsed(
|
||||
offset: widget.groupData.headerData.groupName.length,
|
||||
),
|
||||
text: widget.groupData.headerData.groupName,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_focusNode.addListener(() {
|
||||
if (!_focusNode.hasFocus) {
|
||||
_saveEdit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final boardCustomData = widget.groupData.customData as GroupData;
|
||||
|
||||
return BlocProvider<BoardBloc>.value(
|
||||
value: context.read<BoardBloc>(),
|
||||
child: BlocBuilder<BoardBloc, BoardState>(
|
||||
builder: (context, state) {
|
||||
if (state.isEditingHeader) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_focusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
Widget title = Expanded(
|
||||
child: FlowyText.medium(
|
||||
widget.groupData.headerData.groupName,
|
||||
fontSize: 14,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
);
|
||||
|
||||
if (!boardCustomData.group.isDefault &&
|
||||
boardCustomData.fieldType.canEditHeader) {
|
||||
title = Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: FlowyTooltip(
|
||||
message: LocaleKeys.board_column_renameGroupTooltip.tr(),
|
||||
child: FlowyHover(
|
||||
style: HoverStyle(
|
||||
hoverColor: Colors.transparent,
|
||||
foregroundColorOnHover:
|
||||
AFThemeExtension.of(context).textColor,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () => context.read<BoardBloc>().add(
|
||||
BoardEvent.startEditingHeader(
|
||||
widget.groupData.id,
|
||||
),
|
||||
),
|
||||
child: FlowyText.medium(
|
||||
widget.groupData.headerData.groupName,
|
||||
fontSize: 14,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (state.isEditingHeader &&
|
||||
state.editingHeaderId == widget.groupData.id) {
|
||||
title = _buildTextField(context);
|
||||
}
|
||||
|
||||
return AppFlowyGroupHeader(
|
||||
title: title,
|
||||
icon: _buildHeaderIcon(boardCustomData),
|
||||
addIcon: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: FlowySvg(
|
||||
FlowySvgs.add_s,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
),
|
||||
),
|
||||
onAddButtonClick: () => context
|
||||
.read<BoardBloc>()
|
||||
.add(BoardEvent.createHeaderRow(widget.groupData.id)),
|
||||
height: 50,
|
||||
margin: widget.margin ?? EdgeInsets.zero,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTextField(BuildContext context) {
|
||||
return Expanded(
|
||||
child: RawKeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
onKey: (event) {
|
||||
if (event is RawKeyDownEvent &&
|
||||
[LogicalKeyboardKey.enter, LogicalKeyboardKey.escape]
|
||||
.contains(event.logicalKey)) {
|
||||
_saveEdit();
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
onEditingComplete: _saveEdit,
|
||||
maxLines: 1,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 14),
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
hoverColor: Colors.transparent,
|
||||
// Magic number 4 makes the textField take up the same space as FlowyText
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding + 4,
|
||||
horizontal: 8,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _saveEdit() {
|
||||
context.read<BoardBloc>().add(
|
||||
BoardEvent.endEditingHeader(
|
||||
widget.groupData.id,
|
||||
_controller.text,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget? _buildHeaderIcon(GroupData customData) {
|
||||
Widget? widget;
|
||||
switch (customData.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
final group = customData.asCheckboxGroup()!;
|
||||
widget = FlowySvg(
|
||||
group.isCheck ? FlowySvgs.check_filled_s : FlowySvgs.uncheck_s,
|
||||
blendMode: BlendMode.dst,
|
||||
);
|
||||
break;
|
||||
case FieldType.DateTime:
|
||||
case FieldType.LastEditedTime:
|
||||
case FieldType.CreatedTime:
|
||||
case FieldType.MultiSelect:
|
||||
case FieldType.Number:
|
||||
case FieldType.RichText:
|
||||
case FieldType.SingleSelect:
|
||||
case FieldType.URL:
|
||||
case FieldType.Checklist:
|
||||
break;
|
||||
}
|
||||
|
||||
if (widget != null) {
|
||||
widget = SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: widget,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
@ -53,4 +53,10 @@ extension FieldTypeListExtension on FieldType {
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
|
||||
bool get canEditHeader => switch (this) {
|
||||
FieldType.MultiSelect => true,
|
||||
FieldType.SingleSelect => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class SelectOptionTypeOptionWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
return BlocProvider<SelectOptionTypeOptionBloc>(
|
||||
create: (context) => SelectOptionTypeOptionBloc(
|
||||
options: options,
|
||||
typeOptionAction: typeOptionAction,
|
||||
|
@ -12,9 +12,9 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
required this.cellController,
|
||||
}) : super(TextCellState.initial(cellController)) {
|
||||
on<TextCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
(event, emit) {
|
||||
event.when(
|
||||
initial: () {
|
||||
_startListening();
|
||||
},
|
||||
updateText: (text) {
|
||||
|
Reference in New Issue
Block a user