fix: show newChecklistTask on hover over entire list item in row detail page (#4622)

This commit is contained in:
Richard Shiue 2024-02-07 23:20:26 +08:00 committed by GitHub
parent 3129fa6cc1
commit 88e9d63a13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 53 additions and 75 deletions

View File

@ -10,7 +10,7 @@ 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';
import 'package:provider/provider.dart';
import '../editable_cell_skeleton/checklist.dart';
@ -113,40 +113,34 @@ class _ChecklistItemsState extends State<ChecklistItems> {
),
const VSpace(4),
...children,
const ChecklistItemControl(),
ChecklistItemControl(cellNotifer: widget.cellContainerNotifier),
],
),
);
}
}
class ChecklistItemControl extends StatefulWidget {
const ChecklistItemControl({super.key});
class ChecklistItemControl extends StatelessWidget {
const ChecklistItemControl({super.key, required this.cellNotifer});
@override
State<ChecklistItemControl> createState() => _ChecklistItemControlState();
}
class _ChecklistItemControlState extends State<ChecklistItemControl> {
bool _isHover = false;
final CellContainerNotifier cellNotifer;
@override
Widget build(BuildContext context) {
return MouseRegion(
onHover: (_) => setState(() => _isHover = true),
onExit: (_) => setState(() => _isHover = false),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => context
.read<ChecklistCellBloc>()
.add(const ChecklistCellEvent.createNewTask("")),
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
child: SizedBox(
return ChangeNotifierProvider.value(
value: cellNotifer,
child: Consumer<CellContainerNotifier>(
builder: (buildContext, notifier, _) => GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => context
.read<ChecklistCellBloc>()
.add(const ChecklistCellEvent.createNewTask("")),
child: Container(
margin: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
height: 12,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 150),
child: _isHover
child: notifier.isHover
? FlowyTooltip(
message: LocaleKeys.grid_checklist_addNew.tr(),
child: Row(

View File

@ -190,11 +190,9 @@ class EditableCellBuilder {
}
abstract class CellEditable {
RequestFocusListener get requestFocus;
SingleListenerChangeNotifier get requestFocus;
CellContainerNotifier get cellContainerNotifier;
// ValueNotifier<bool> get onCellEditing;
}
typedef AccessoryBuilder = List<GridCellAccessoryBuilder> Function(
@ -204,9 +202,6 @@ typedef AccessoryBuilder = List<GridCellAccessoryBuilder> Function(
abstract class CellAccessory extends Widget {
const CellAccessory({super.key});
// The hover will show if the isHover's value is true
ValueNotifier<bool>? get onAccessoryHover;
AccessoryBuilder? get accessoryBuilder;
}
@ -217,20 +212,11 @@ abstract class EditableCellWidget extends StatefulWidget
@override
final CellContainerNotifier cellContainerNotifier = CellContainerNotifier();
// When the cell is focused, we assume that the accessory also be hovered.
@override
ValueNotifier<bool> get onAccessoryHover => ValueNotifier(false);
// @override
// final ValueNotifier<bool> onCellEditing = ValueNotifier<bool>(false);
AccessoryBuilder? get accessoryBuilder => null;
@override
List<GridCellAccessoryBuilder> Function(
GridCellAccessoryBuildContext buildContext,
)? get accessoryBuilder => null;
@override
final RequestFocusListener requestFocus = RequestFocusListener();
final requestFocus = SingleListenerChangeNotifier();
@override
final Map<CellKeyboardKey, CellKeyboardAction> shortcutHandlers = {};
@ -240,28 +226,25 @@ abstract class GridCellState<T extends EditableCellWidget> extends State<T> {
@override
void initState() {
super.initState();
widget.requestFocus.setListener(requestBeginFocus);
widget.requestFocus.addListener(onRequestFocus);
}
@override
void didUpdateWidget(covariant T oldWidget) {
if (oldWidget != this) {
widget.requestFocus.setListener(requestBeginFocus);
widget.requestFocus.addListener(onRequestFocus);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
widget.onAccessoryHover.dispose();
widget.requestFocus.removeAllListener();
widget.requestFocus.dispose();
super.dispose();
}
/// Subclass can override this method to request focus.
void requestBeginFocus();
void onRequestFocus();
String? onCopy() => null;
}
@ -287,7 +270,7 @@ abstract class GridEditableTextCell<T extends EditableCellWidget>
}
@override
void requestBeginFocus() {
void onRequestFocus() {
if (!focusNode.hasFocus && focusNode.canRequestFocus) {
FocusScope.of(context).requestFocus(focusNode);
}
@ -304,28 +287,25 @@ abstract class GridEditableTextCell<T extends EditableCellWidget>
Future<void> focusChanged() async {}
}
class RequestFocusListener extends ChangeNotifier {
class SingleListenerChangeNotifier extends ChangeNotifier {
VoidCallback? _listener;
void setListener(VoidCallback listener) {
@override
void addListener(VoidCallback listener) {
if (_listener != null) {
removeListener(_listener!);
}
_listener = listener;
addListener(listener);
super.addListener(listener);
}
void removeAllListener() {
if (_listener != null) {
removeListener(_listener!);
_listener = null;
}
@override
void dispose() {
_listener = null;
super.dispose();
}
void notify() {
notifyListeners();
}
void notify() => notifyListeners();
}
class SingleListenerFocusNode extends FocusNode {
@ -374,7 +354,7 @@ class EditableCellSkinMap {
FieldType.Checklist => checklistSkin != null,
FieldType.CreatedTime ||
FieldType.LastEditedTime =>
throw timestampSkin != null,
timestampSkin != null,
FieldType.DateTime => dateSkin != null,
FieldType.MultiSelect ||
FieldType.SingleSelect =>

View File

@ -80,9 +80,7 @@ class _CheckboxCellState extends GridCellState<EditableCheckboxCell> {
}
@override
void requestBeginFocus() {
cellBloc.add(const CheckboxCellEvent.select());
}
void onRequestFocus() => cellBloc.add(const CheckboxCellEvent.select());
@override
String? onCopy() {

View File

@ -85,7 +85,7 @@ class GridChecklistCellState extends GridCellState<EditableChecklistCell> {
}
@override
void requestBeginFocus() {
void onRequestFocus() {
if (widget.skin is DesktopGridChecklistCellSkin) {
_popover.show();
}

View File

@ -84,7 +84,7 @@ class _DateCellState extends GridCellState<EditableDateCell> {
}
@override
void requestBeginFocus() {
void onRequestFocus() {
_popover.show();
widget.cellContainerNotifier.isFocus = true;
}

View File

@ -96,7 +96,7 @@ class _NumberCellState extends GridEditableTextCell<EditableNumberCell> {
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
@override
void requestBeginFocus() {
void onRequestFocus() {
focusNode.requestFocus();
}

View File

@ -92,5 +92,5 @@ class _SelectOptionCellState extends GridCellState<EditableSelectOptionCell> {
}
@override
void requestBeginFocus() => _popover.show();
void onRequestFocus() => _popover.show();
}

View File

@ -97,7 +97,7 @@ class _TextCellState extends GridEditableTextCell<EditableTextCell> {
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
@override
void requestBeginFocus() {
void onRequestFocus() {
focusNode.requestFocus();
}

View File

@ -83,7 +83,7 @@ class _TimestampCellState extends GridCellState<EditableTimestampCell> {
}
@override
void requestBeginFocus() {
void onRequestFocus() {
widget.cellContainerNotifier.isFocus = true;
}

View File

@ -98,7 +98,7 @@ class _GridCellEnterRegion extends StatelessWidget {
return Selector2<RegionStateNotifier, CellContainerNotifier, bool>(
selector: (context, regionNotifier, cellNotifier) =>
!cellNotifier.isFocus &&
(cellNotifier.onEnter || regionNotifier.onEnter && isPrimary),
(cellNotifier.isHover || regionNotifier.onEnter && isPrimary),
builder: (context, showAccessory, _) {
final List<Widget> children = [child];
@ -113,9 +113,9 @@ class _GridCellEnterRegion extends StatelessWidget {
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (p) =>
CellContainerNotifier.of(context, listen: false).onEnter = true,
CellContainerNotifier.of(context, listen: false).isHover = true,
onExit: (p) =>
CellContainerNotifier.of(context, listen: false).onEnter = false,
CellContainerNotifier.of(context, listen: false).isHover = false,
child: Stack(
alignment: AlignmentDirectional.center,
fit: StackFit.expand,
@ -138,7 +138,7 @@ class CellContainerNotifier extends ChangeNotifier {
}
}
set onEnter(bool value) {
set isHover(bool value) {
if (_onEnter != value) {
_onEnter = value;
notifyListeners();
@ -147,7 +147,7 @@ class CellContainerNotifier extends ChangeNotifier {
bool get isFocus => _isFocus;
bool get onEnter => _onEnter;
bool get isHover => _onEnter;
static CellContainerNotifier of(BuildContext context, {bool listen = true}) {
return Provider.of<CellContainerNotifier>(context, listen: listen);

View File

@ -189,8 +189,14 @@ class _PropertyCellState extends State<_PropertyCell> {
margin: const EdgeInsets.only(bottom: 8),
constraints: const BoxConstraints(minHeight: 30),
child: MouseRegion(
onEnter: (event) => _isFieldHover.value = true,
onExit: (event) => _isFieldHover.value = false,
onEnter: (event) {
_isFieldHover.value = true;
cell.cellContainerNotifier.isHover = true;
},
onExit: (event) {
_isFieldHover.value = false;
cell.cellContainerNotifier.isHover = false;
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [