Merge pull request #944 from AppFlowy-IO/feat/edit_card_button

chore: add edit card button
This commit is contained in:
Nathan.fooo 2022-08-30 23:08:31 +08:00 committed by GitHub
commit b61e3f4601
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 178 additions and 34 deletions

View File

@ -27,6 +27,9 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
emit(state.copyWith(content: text));
}
},
enableEdit: (bool enabled) {
emit(state.copyWith(enableEdit: enabled));
},
);
},
);
@ -57,6 +60,7 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
class BoardTextCellEvent with _$BoardTextCellEvent {
const factory BoardTextCellEvent.initial() = _InitialCell;
const factory BoardTextCellEvent.updateText(String text) = _UpdateContent;
const factory BoardTextCellEvent.enableEdit(bool enabled) = _EnableEdit;
const factory BoardTextCellEvent.didReceiveCellUpdate(String cellContent) =
_DidReceiveCellUpdate;
}
@ -65,10 +69,12 @@ class BoardTextCellEvent with _$BoardTextCellEvent {
class BoardTextCellState with _$BoardTextCellState {
const factory BoardTextCellState({
required String content,
required bool enableEdit,
}) = _BoardTextCellState;
factory BoardTextCellState.initial(GridCellController context) =>
BoardTextCellState(
content: context.getCellData() ?? "",
enableEdit: false,
);
}

View File

@ -107,7 +107,10 @@ class BoardCardState with _$BoardCardState {
factory BoardCardState.initial(
RowPB rowPB, UnmodifiableListView<BoardCellEquatable> cells) =>
BoardCardState(rowPB: rowPB, cells: cells);
BoardCardState(
rowPB: rowPB,
cells: cells,
);
}
class BoardCellEquatable extends Equatable {

View File

@ -1,3 +1,54 @@
import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'package:flowy_infra/notifier.dart';
abstract class FocusableBoardCell {
set becomeFocus(bool isFocus);
}
class EditableCellNotifier {
final Notifier becomeFirstResponder = Notifier();
final Notifier resignFirstResponder = Notifier();
EditableCellNotifier();
}
class EditableRowNotifier {
Map<EditableCellId, EditableCellNotifier> cells = {};
void insertCell(
GridCellIdentifier cellIdentifier,
EditableCellNotifier notifier,
) {
cells[EditableCellId.from(cellIdentifier)] = notifier;
}
void becomeFirstResponder() {
for (final notifier in cells.values) {
notifier.becomeFirstResponder.notify();
}
}
void resignFirstResponder() {
for (final notifier in cells.values) {
notifier.resignFirstResponder.notify();
}
}
}
abstract class EditableCell {
EditableCellNotifier? get editableNotifier;
}
class EditableCellId {
String fieldId;
String rowId;
EditableCellId(this.rowId, this.fieldId);
factory EditableCellId.from(GridCellIdentifier cellIdentifier) =>
EditableCellId(
cellIdentifier.rowId,
cellIdentifier.fieldId,
);
}

View File

@ -5,13 +5,18 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/cell/select_option_c
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class BoardSelectOptionCell extends StatefulWidget {
import 'board_cell.dart';
class BoardSelectOptionCell extends StatefulWidget with EditableCell {
final String groupId;
final GridCellControllerBuilder cellControllerBuilder;
@override
final EditableCellNotifier? editableNotifier;
const BoardSelectOptionCell({
required this.groupId,
required this.cellControllerBuilder,
this.editableNotifier,
Key? key,
}) : super(key: key);

View File

@ -4,15 +4,19 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/cell/cell_builder.da
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class BoardTextCell extends StatefulWidget {
import 'board_cell.dart';
class BoardTextCell extends StatefulWidget with EditableCell {
final String groupId;
final bool isFocus;
@override
final EditableCellNotifier? editableNotifier;
final GridCellControllerBuilder cellControllerBuilder;
const BoardTextCell({
required this.groupId,
required this.cellControllerBuilder,
this.editableNotifier,
this.isFocus = false,
Key? key,
}) : super(key: key);
@ -37,6 +41,18 @@ class _BoardTextCellState extends State<BoardTextCell> {
if (widget.isFocus) {
focusNode.requestFocus();
}
widget.editableNotifier?.becomeFirstResponder.addListener(() {
if (!mounted) return;
focusNode.requestFocus();
_cellBloc.add(const BoardTextCellEvent.enableEdit(true));
});
widget.editableNotifier?.resignFirstResponder.addListener(() {
if (!mounted) return;
_cellBloc.add(const BoardTextCellEvent.enableEdit(false));
});
super.initState();
}
@ -50,18 +66,26 @@ class _BoardTextCellState extends State<BoardTextCell> {
_controller.text = state.content;
}
},
child: TextField(
controller: _controller,
focusNode: focusNode,
onChanged: (value) => focusChanged(),
onEditingComplete: () => focusNode.unfocus(),
maxLines: 1,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 6),
border: InputBorder.none,
isDense: true,
),
child: BlocBuilder<BoardTextCellBloc, BoardTextCellState>(
buildWhen: (previous, current) =>
previous.enableEdit != current.enableEdit,
builder: (context, state) {
return TextField(
// autofocus: true,
// enabled: state.enableEdit,
controller: _controller,
focusNode: focusNode,
onChanged: (value) => focusChanged(),
onEditingComplete: () => focusNode.unfocus(),
maxLines: 1,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 6),
border: InputBorder.none,
isDense: true,
),
);
},
),
),
);

View File

@ -7,6 +7,7 @@ import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'board_cell.dart';
import 'card_cell_builder.dart';
import 'card_container.dart';
@ -36,9 +37,11 @@ class BoardCard extends StatefulWidget {
class _BoardCardState extends State<BoardCard> {
late BoardCardBloc _cardBloc;
late EditableRowNotifier rowNotifier;
@override
void initState() {
rowNotifier = EditableRowNotifier();
_cardBloc = BoardCardBloc(
gridId: widget.gridId,
fieldId: widget.fieldId,
@ -58,7 +61,12 @@ class _BoardCardState extends State<BoardCard> {
builder: (context, state) {
return BoardCardContainer(
accessoryBuilder: (context) {
return [const _CardMoreOption()];
return [
_CardEditOption(
startEditing: () => rowNotifier.becomeFirstResponder(),
),
const _CardMoreOption(),
];
},
onTap: (context) {
widget.openCard(context);
@ -83,11 +91,14 @@ class _BoardCardState extends State<BoardCard> {
final List<Widget> children = [];
cells.asMap().forEach(
(int index, GridCellIdentifier cellId) {
final cellNotifier = EditableCellNotifier();
Widget child = widget.cellBuilder.buildCell(
widget.groupId,
cellId,
widget.isEditing,
cellNotifier,
);
rowNotifier.insertCell(cellId, cellNotifier);
if (index != 0) {
child = Padding(
@ -121,7 +132,11 @@ class _CardMoreOption extends StatelessWidget with CardAccessory {
@override
Widget build(BuildContext context) {
return svgWidget('grid/details', color: context.read<AppTheme>().iconColor);
return Padding(
padding: const EdgeInsets.all(3.0),
child:
svgWidget('grid/details', color: context.read<AppTheme>().iconColor),
);
}
@override
@ -131,3 +146,27 @@ class _CardMoreOption extends StatelessWidget with CardAccessory {
).show(context, direction: AnchorDirection.bottomWithCenterAligned);
}
}
class _CardEditOption extends StatelessWidget with CardAccessory {
final VoidCallback startEditing;
const _CardEditOption({
required this.startEditing,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(3.0),
child: svgWidget(
'editor/edit',
color: context.read<AppTheme>().iconColor,
),
);
}
@override
void onTap(BuildContext context) {
startEditing();
}
}

View File

@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter/material.dart';
import 'board_cell.dart';
import 'board_checkbox_cell.dart';
import 'board_date_cell.dart';
import 'board_number_cell.dart';
@ -23,6 +24,7 @@ class BoardCellBuilder {
String groupId,
GridCellIdentifier cellId,
bool isEditing,
EditableCellNotifier cellNotifier,
) {
final cellControllerBuilder = GridCellControllerBuilder(
delegate: delegate,
@ -54,6 +56,7 @@ class BoardCellBuilder {
return BoardSelectOptionCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
editableNotifier: cellNotifier,
key: key,
);
case FieldType.Number:
@ -67,6 +70,7 @@ class BoardCellBuilder {
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
isFocus: isEditing,
editableNotifier: cellNotifier,
key: key,
);
case FieldType.URL:

View File

@ -69,12 +69,11 @@ class CardAccessoryContainer extends StatelessWidget {
style: HoverStyle(
hoverColor: theme.hover,
backgroundColor: theme.surface,
borderRadius: BorderRadius.zero,
),
builder: (_, onHover) => Container(
width: 26,
height: 26,
padding: const EdgeInsets.all(3),
decoration: _makeBoxDecoration(context),
builder: (_, onHover) => SizedBox(
width: 24,
height: 24,
child: accessory,
),
);
@ -85,7 +84,11 @@ class CardAccessoryContainer extends StatelessWidget {
);
}).toList();
return Wrap(children: children, spacing: 6);
return Container(
clipBehavior: Clip.hardEdge,
decoration: _makeBoxDecoration(context),
child: Row(children: children),
);
}
}
@ -95,15 +98,16 @@ BoxDecoration _makeBoxDecoration(BuildContext context) {
return BoxDecoration(
color: Colors.transparent,
border: Border.fromBorderSide(borderSide),
boxShadow: const [
BoxShadow(
color: Colors.transparent,
spreadRadius: 0,
blurRadius: 2,
offset: Offset.zero,
)
],
borderRadius: const BorderRadius.all(Radius.circular(6)),
// boxShadow: const [
// BoxShadow(
// color: Colors.transparent,
// spreadRadius: 0,
// blurRadius: 5,
// offset: Offset.zero,
// )
// ],
borderRadius: const BorderRadius.all(Radius.circular(4)),
);
}

View File

@ -31,7 +31,8 @@ class PublishNotifier<T> extends ChangeNotifier {
T? get currentValue => _value;
void addPublishListener(void Function(T) callback, {bool Function()? listenWhen}) {
void addPublishListener(void Function(T) callback,
{bool Function()? listenWhen}) {
super.addListener(
() {
if (_value == null) {
@ -47,3 +48,9 @@ class PublishNotifier<T> extends ChangeNotifier {
);
}
}
class Notifier extends ChangeNotifier {
void notify() {
notifyListeners();
}
}

View File

@ -154,6 +154,7 @@ pub fn select_option_color_from_index(index: usize) -> SelectOptionColorPB {
_ => SelectOptionColorPB::Purple,
}
}
pub struct SelectOptionIds(Vec<String>);
impl SelectOptionIds {