Skip to content

Commit

Permalink
feat: show checklist items inline in row page (AppFlowy-IO#3737)
Browse files Browse the repository at this point in the history
* feat: show checklist items inline in row page

* fix: tauri build
  • Loading branch information
richardshiue authored Oct 24, 2023
1 parent 25a98cd commit 6c3d7d2
Show file tree
Hide file tree
Showing 20 changed files with 402 additions and 436 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ extension AppFlowyDatabaseTest on WidgetTester {

final widget = this.widget<ChecklistItem>(task);
assert(
widget.option.data.name == name && widget.option.isSelected == isChecked,
widget.task.data.name == name && widget.task.isSelected == isChecked,
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/cell_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
Expand Down Expand Up @@ -69,13 +68,4 @@ class ChecklistCellBackendService {

return DatabaseEventUpdateChecklistCell(payload).send();
}

Future<Either<ChecklistCellDataPB, FlowyError>> getCellData() {
final payload = CellIdPB.create()
..viewId = viewId
..fieldId = fieldId
..rowId = rowId;

return DatabaseEventGetChecklistCellData(payload).send();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,10 @@ class _PropertyCellState extends State<PropertyCell> {
final gesture = GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => cell.requestFocus.notify(),
child: AccessoryHover(child: cell),
child: AccessoryHover(
fieldType: widget.cellContext.fieldType,
child: cell,
),
);

return Container(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ class _ChecklistCellState extends State<ChecklistCardCell> {
value: _cellBloc,
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
builder: (context, state) {
if (state.allOptions.isEmpty) {
if (state.tasks.isEmpty) {
return const SizedBox.shrink();
}

return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: ChecklistProgressBar(percent: state.percent),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart';

Expand Down Expand Up @@ -92,7 +93,12 @@ class _PrimaryCellAccessoryState extends State<PrimaryCellAccessory>

class AccessoryHover extends StatefulWidget {
final CellAccessory child;
const AccessoryHover({required this.child, super.key});
final FieldType fieldType;
const AccessoryHover({
super.key,
required this.child,
required this.fieldType,
});

@override
State<AccessoryHover> createState() => _AccessoryHoverState();
Expand All @@ -106,7 +112,7 @@ class _AccessoryHoverState extends State<AccessoryHover> {
final List<Widget> children = [
DecoratedBox(
decoration: BoxDecoration(
color: _isHover
color: _isHover && widget.fieldType != FieldType.Checklist
? AFThemeExtension.of(context).lightGreyHover
: Colors.transparent,
borderRadius: Corners.s6Border,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

Expand All @@ -11,24 +16,30 @@ import 'checklist_cell_editor.dart';
import 'checklist_progress_bar.dart';

class ChecklistCellStyle extends GridCellStyle {
String placeholder;
EdgeInsets? cellPadding;
final String placeholder;
final EdgeInsets? cellPadding;
final bool showTasksInline;

ChecklistCellStyle({
required this.placeholder,
const ChecklistCellStyle({
this.placeholder = "",
this.cellPadding,
this.showTasksInline = false,
});
}

class GridChecklistCell extends GridCellWidget {
final CellControllerBuilder cellControllerBuilder;
late final ChecklistCellStyle? cellStyle;
late final ChecklistCellStyle cellStyle;
GridChecklistCell({
required this.cellControllerBuilder,
GridCellStyle? style,
super.key,
}) {
cellStyle = style as ChecklistCellStyle?;
if (style != null) {
cellStyle = (style as ChecklistCellStyle);
} else {
cellStyle = const ChecklistCellStyle();
}
}

@override
Expand All @@ -38,59 +49,169 @@ class GridChecklistCell extends GridCellWidget {
class GridChecklistCellState extends GridCellState<GridChecklistCell> {
late ChecklistCellBloc _cellBloc;
late final PopoverController _popover;
bool showIncompleteOnly = false;

@override
void initState() {
_popover = PopoverController();
final cellController =
widget.cellControllerBuilder.build() as ChecklistCellController;
_cellBloc = ChecklistCellBloc(cellController: cellController);
_cellBloc.add(const ChecklistCellEvent.initial());
_cellBloc = ChecklistCellBloc(cellController: cellController)
..add(const ChecklistCellEvent.initial());
super.initState();
}

@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cellBloc,
child: AppFlowyPopover(
margin: EdgeInsets.zero,
controller: _popover,
constraints: BoxConstraints.loose(const Size(360, 400)),
direction: PopoverDirection.bottomWithLeftAligned,
triggerActions: PopoverTriggerFlags.none,
popupBuilder: (BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.onCellFocus.value = true;
});
return GridChecklistCellEditor(
cellController:
widget.cellControllerBuilder.build() as ChecklistCellController,
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
builder: (context, state) {
if (widget.cellStyle.showTasksInline) {
final tasks = List.from(state.tasks);
if (showIncompleteOnly) {
tasks.removeWhere((task) => task.isSelected);
}
final children = tasks
.mapIndexed(
(index, task) => ChecklistItem(
task: task,
autofocus: state.newTask && index == tasks.length - 1,
),
)
.toList();
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding:
widget.cellStyle.cellPadding ?? GridSize.cellContentInsets,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: ChecklistProgressBar(percent: state.percent),
),
const HSpace(6.0),
FlowyIconButton(
tooltipText: showIncompleteOnly
? LocaleKeys.grid_checklist_showComplete.tr()
: LocaleKeys.grid_checklist_hideComplete.tr(),
width: 32,
iconColorOnHover:
Theme.of(context).colorScheme.onSecondary,
icon: FlowySvg(
showIncompleteOnly
? FlowySvgs.show_m
: FlowySvgs.hide_m,
size: const Size.square(16),
),
onPressed: () {
setState(
() => showIncompleteOnly = !showIncompleteOnly,
);
},
),
],
),
),
const VSpace(4),
...children,
const ChecklistItemControl(),
],
),
),
);
}

return AppFlowyPopover(
margin: EdgeInsets.zero,
controller: _popover,
constraints: BoxConstraints.loose(const Size(360, 400)),
direction: PopoverDirection.bottomWithLeftAligned,
triggerActions: PopoverTriggerFlags.none,
popupBuilder: (BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.onCellFocus.value = true;
});
return GridChecklistCellEditor(
cellController: widget.cellControllerBuilder.build()
as ChecklistCellController,
);
},
onClose: () => widget.onCellFocus.value = false,
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding:
widget.cellStyle.cellPadding ?? GridSize.cellContentInsets,
child: state.tasks.isEmpty
? FlowyText.medium(
widget.cellStyle.placeholder,
color: Theme.of(context).hintColor,
)
: ChecklistProgressBar(percent: state.percent),
),
),
);
},
onClose: () => widget.onCellFocus.value = false,
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding:
widget.cellStyle?.cellPadding ?? GridSize.cellContentInsets,
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
builder: (context, state) {
if (state.allOptions.isEmpty) {
return FlowyText.medium(
widget.cellStyle?.placeholder ?? "",
color: Theme.of(context).hintColor,
);
}
return ChecklistProgressBar(percent: state.percent);
},
),
),
),
),
);
}

@override
void requestBeginFocus() => _popover.show();
void requestBeginFocus() {
if (!widget.cellStyle.showTasksInline) {
_popover.show();
}
}
}

class ChecklistItemControl extends StatelessWidget {
const ChecklistItemControl({super.key});

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
child: SizedBox(
height: 12,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => context
.read<ChecklistCellBloc>()
.add(const ChecklistCellEvent.createNewTask("")),
child: Row(
children: [
const Flexible(child: Center(child: Divider())),
const HSpace(12.0),
FlowyTooltip(
message: LocaleKeys.grid_checklist_addNew.tr(),
child: FilledButton(
style: FilledButton.styleFrom(
minimumSize: const Size.square(12),
maximumSize: const Size.square(12),
padding: EdgeInsets.zero,
),
onPressed: () => context
.read<ChecklistCellBloc>()
.add(const ChecklistCellEvent.createNewTask("")),
child: FlowySvg(
FlowySvgs.add_s,
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
const HSpace(12.0),
const Flexible(child: Center(child: Divider())),
],
),
),
),
);
}
}
Loading

0 comments on commit 6c3d7d2

Please sign in to comment.