mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: show newChecklistTask on hover over entire list item in row detail page (#4622)
This commit is contained in:
parent
3129fa6cc1
commit
88e9d63a13
@ -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(
|
||||
|
@ -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 =>
|
||||
|
@ -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() {
|
||||
|
@ -85,7 +85,7 @@ class GridChecklistCellState extends GridCellState<EditableChecklistCell> {
|
||||
}
|
||||
|
||||
@override
|
||||
void requestBeginFocus() {
|
||||
void onRequestFocus() {
|
||||
if (widget.skin is DesktopGridChecklistCellSkin) {
|
||||
_popover.show();
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ class _DateCellState extends GridCellState<EditableDateCell> {
|
||||
}
|
||||
|
||||
@override
|
||||
void requestBeginFocus() {
|
||||
void onRequestFocus() {
|
||||
_popover.show();
|
||||
widget.cellContainerNotifier.isFocus = true;
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ class _NumberCellState extends GridEditableTextCell<EditableNumberCell> {
|
||||
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
|
||||
|
||||
@override
|
||||
void requestBeginFocus() {
|
||||
void onRequestFocus() {
|
||||
focusNode.requestFocus();
|
||||
}
|
||||
|
||||
|
@ -92,5 +92,5 @@ class _SelectOptionCellState extends GridCellState<EditableSelectOptionCell> {
|
||||
}
|
||||
|
||||
@override
|
||||
void requestBeginFocus() => _popover.show();
|
||||
void onRequestFocus() => _popover.show();
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ class _TextCellState extends GridEditableTextCell<EditableTextCell> {
|
||||
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
|
||||
|
||||
@override
|
||||
void requestBeginFocus() {
|
||||
void onRequestFocus() {
|
||||
focusNode.requestFocus();
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ class _TimestampCellState extends GridCellState<EditableTimestampCell> {
|
||||
}
|
||||
|
||||
@override
|
||||
void requestBeginFocus() {
|
||||
void onRequestFocus() {
|
||||
widget.cellContainerNotifier.isFocus = true;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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: [
|
||||
|
Loading…
Reference in New Issue
Block a user