mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-07-23 09:01:21 +00:00
288 lines
8.0 KiB
Dart
288 lines
8.0 KiB
Dart
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
|
import 'package:appflowy/mobile/presentation/database/card/card.dart';
|
|
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
|
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
|
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
|
import 'package:appflowy/plugins/database/grid/presentation/widgets/row/action.dart';
|
|
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import 'card_bloc.dart';
|
|
import '../cell/card_cell_builder.dart';
|
|
import '../cell/card_cell_skeleton/card_cell.dart';
|
|
import 'container/accessory.dart';
|
|
import 'container/card_container.dart';
|
|
|
|
/// Edit a database row with card style widget
|
|
class RowCard extends StatefulWidget {
|
|
const RowCard({
|
|
super.key,
|
|
required this.fieldController,
|
|
required this.rowMeta,
|
|
required this.viewId,
|
|
required this.isEditing,
|
|
required this.rowCache,
|
|
required this.cellBuilder,
|
|
required this.openCard,
|
|
required this.onStartEditing,
|
|
required this.onEndEditing,
|
|
required this.styleConfiguration,
|
|
this.groupingFieldId,
|
|
this.groupId,
|
|
});
|
|
|
|
final FieldController fieldController;
|
|
final RowMetaPB rowMeta;
|
|
final String viewId;
|
|
final String? groupingFieldId;
|
|
final String? groupId;
|
|
|
|
final bool isEditing;
|
|
final RowCache rowCache;
|
|
|
|
/// The [CardCellBuilder] is used to build the card cells.
|
|
final CardCellBuilder cellBuilder;
|
|
|
|
/// Called when the user taps on the card.
|
|
final void Function(BuildContext) openCard;
|
|
|
|
/// Called when the user starts editing the card.
|
|
final VoidCallback onStartEditing;
|
|
|
|
/// Called when the user ends editing the card.
|
|
final VoidCallback onEndEditing;
|
|
|
|
final RowCardStyleConfiguration styleConfiguration;
|
|
|
|
@override
|
|
State<RowCard> createState() => _RowCardState();
|
|
}
|
|
|
|
class _RowCardState extends State<RowCard> {
|
|
final popoverController = PopoverController();
|
|
late final CardBloc _cardBloc;
|
|
late final EditableRowNotifier rowNotifier;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
rowNotifier = EditableRowNotifier(isEditing: widget.isEditing);
|
|
_cardBloc = CardBloc(
|
|
fieldController: widget.fieldController,
|
|
viewId: widget.viewId,
|
|
groupFieldId: widget.groupingFieldId,
|
|
isEditing: widget.isEditing,
|
|
rowMeta: widget.rowMeta,
|
|
rowCache: widget.rowCache,
|
|
)..add(const CardEvent.initial());
|
|
|
|
rowNotifier.isEditing.addListener(() {
|
|
if (!mounted) return;
|
|
_cardBloc.add(CardEvent.setIsEditing(rowNotifier.isEditing.value));
|
|
|
|
if (rowNotifier.isEditing.value) {
|
|
widget.onStartEditing();
|
|
} else {
|
|
widget.onEndEditing();
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
rowNotifier.dispose();
|
|
_cardBloc.close();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocProvider.value(
|
|
value: _cardBloc,
|
|
child: BlocBuilder<CardBloc, CardState>(
|
|
builder: (context, state) =>
|
|
PlatformExtension.isMobile ? _mobile(state) : _desktop(state),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _mobile(CardState state) {
|
|
return GestureDetector(
|
|
onTap: () => widget.openCard(context),
|
|
behavior: HitTestBehavior.opaque,
|
|
child: MobileCardContent(
|
|
rowMeta: state.rowMeta,
|
|
cellBuilder: widget.cellBuilder,
|
|
styleConfiguration: widget.styleConfiguration,
|
|
cells: state.cells,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _desktop(CardState state) {
|
|
final accessories = widget.styleConfiguration.showAccessory
|
|
? <CardAccessory>[
|
|
EditCardAccessory(rowNotifier: rowNotifier),
|
|
const MoreCardOptionsAccessory(),
|
|
]
|
|
: null;
|
|
return AppFlowyPopover(
|
|
controller: popoverController,
|
|
triggerActions: PopoverTriggerFlags.none,
|
|
constraints: BoxConstraints.loose(const Size(140, 200)),
|
|
direction: PopoverDirection.rightWithCenterAligned,
|
|
popupBuilder: (_) {
|
|
return RowActionMenu.board(
|
|
viewId: _cardBloc.viewId,
|
|
rowId: _cardBloc.rowId,
|
|
groupId: widget.groupId,
|
|
);
|
|
},
|
|
child: RowCardContainer(
|
|
buildAccessoryWhen: () => state.isEditing == false,
|
|
accessories: accessories ?? [],
|
|
openAccessory: _handleOpenAccessory,
|
|
openCard: widget.openCard,
|
|
child: _CardContent(
|
|
rowMeta: state.rowMeta,
|
|
rowNotifier: rowNotifier,
|
|
cellBuilder: widget.cellBuilder,
|
|
styleConfiguration: widget.styleConfiguration,
|
|
cells: state.cells,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _handleOpenAccessory(AccessoryType newAccessoryType) {
|
|
switch (newAccessoryType) {
|
|
case AccessoryType.edit:
|
|
break;
|
|
case AccessoryType.more:
|
|
popoverController.show();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
class _CardContent extends StatelessWidget {
|
|
const _CardContent({
|
|
required this.rowMeta,
|
|
required this.rowNotifier,
|
|
required this.cellBuilder,
|
|
required this.cells,
|
|
required this.styleConfiguration,
|
|
});
|
|
|
|
final RowMetaPB rowMeta;
|
|
final EditableRowNotifier rowNotifier;
|
|
final CardCellBuilder cellBuilder;
|
|
final List<CellContext> cells;
|
|
final RowCardStyleConfiguration styleConfiguration;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final child = Padding(
|
|
padding: styleConfiguration.cardPadding,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: _makeCells(context, rowMeta, cells),
|
|
),
|
|
);
|
|
return styleConfiguration.hoverStyle == null
|
|
? child
|
|
: FlowyHover(
|
|
style: styleConfiguration.hoverStyle,
|
|
buildWhenOnHover: () => !rowNotifier.isEditing.value,
|
|
child: child,
|
|
);
|
|
}
|
|
|
|
List<Widget> _makeCells(
|
|
BuildContext context,
|
|
RowMetaPB rowMeta,
|
|
List<CellContext> cells,
|
|
) {
|
|
// Remove all the cell listeners.
|
|
rowNotifier.unbind();
|
|
|
|
return cells.mapIndexed((int index, CellContext cellContext) {
|
|
EditableCardNotifier? cellNotifier;
|
|
|
|
if (index == 0) {
|
|
cellNotifier =
|
|
EditableCardNotifier(isEditing: rowNotifier.isEditing.value);
|
|
rowNotifier.bindCell(cellContext, cellNotifier);
|
|
}
|
|
|
|
return cellBuilder.build(
|
|
cellContext: cellContext,
|
|
cellNotifier: cellNotifier,
|
|
styleMap: styleConfiguration.cellStyleMap,
|
|
hasNotes: !rowMeta.isDocumentEmpty,
|
|
);
|
|
}).toList();
|
|
}
|
|
}
|
|
|
|
class MoreCardOptionsAccessory extends StatelessWidget with CardAccessory {
|
|
const MoreCardOptionsAccessory({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(3.0),
|
|
child: FlowySvg(
|
|
FlowySvgs.three_dots_s,
|
|
color: Theme.of(context).hintColor,
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
AccessoryType get type => AccessoryType.more;
|
|
}
|
|
|
|
class EditCardAccessory extends StatelessWidget with CardAccessory {
|
|
const EditCardAccessory({super.key, required this.rowNotifier});
|
|
|
|
final EditableRowNotifier rowNotifier;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(3.0),
|
|
child: FlowySvg(
|
|
FlowySvgs.edit_s,
|
|
color: Theme.of(context).hintColor,
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void onTap(BuildContext context) => rowNotifier.becomeFirstResponder();
|
|
|
|
@override
|
|
AccessoryType get type => AccessoryType.edit;
|
|
}
|
|
|
|
class RowCardStyleConfiguration {
|
|
const RowCardStyleConfiguration({
|
|
required this.cellStyleMap,
|
|
this.showAccessory = true,
|
|
this.cardPadding = const EdgeInsets.all(8),
|
|
this.hoverStyle,
|
|
});
|
|
|
|
final CardCellStyleMap cellStyleMap;
|
|
final bool showAccessory;
|
|
final EdgeInsets cardPadding;
|
|
final HoverStyle? hoverStyle;
|
|
}
|