mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #944 from AppFlowy-IO/feat/edit_card_button
chore: add edit card button
This commit is contained in:
commit
b61e3f4601
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +154,7 @@ pub fn select_option_color_from_index(index: usize) -> SelectOptionColorPB {
|
||||
_ => SelectOptionColorPB::Purple,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SelectOptionIds(Vec<String>);
|
||||
|
||||
impl SelectOptionIds {
|
||||
|
Loading…
Reference in New Issue
Block a user