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/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../editable_cell_skeleton/checklist.dart';
|
import '../editable_cell_skeleton/checklist.dart';
|
||||||
|
|
||||||
@ -113,40 +113,34 @@ class _ChecklistItemsState extends State<ChecklistItems> {
|
|||||||
),
|
),
|
||||||
const VSpace(4),
|
const VSpace(4),
|
||||||
...children,
|
...children,
|
||||||
const ChecklistItemControl(),
|
ChecklistItemControl(cellNotifer: widget.cellContainerNotifier),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChecklistItemControl extends StatefulWidget {
|
class ChecklistItemControl extends StatelessWidget {
|
||||||
const ChecklistItemControl({super.key});
|
const ChecklistItemControl({super.key, required this.cellNotifer});
|
||||||
|
|
||||||
@override
|
final CellContainerNotifier cellNotifer;
|
||||||
State<ChecklistItemControl> createState() => _ChecklistItemControlState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChecklistItemControlState extends State<ChecklistItemControl> {
|
|
||||||
bool _isHover = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MouseRegion(
|
return ChangeNotifierProvider.value(
|
||||||
onHover: (_) => setState(() => _isHover = true),
|
value: cellNotifer,
|
||||||
onExit: (_) => setState(() => _isHover = false),
|
child: Consumer<CellContainerNotifier>(
|
||||||
child: GestureDetector(
|
builder: (buildContext, notifier, _) => GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () => context
|
onTap: () => context
|
||||||
.read<ChecklistCellBloc>()
|
.read<ChecklistCellBloc>()
|
||||||
.add(const ChecklistCellEvent.createNewTask("")),
|
.add(const ChecklistCellEvent.createNewTask("")),
|
||||||
child: Padding(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
|
margin: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
|
||||||
child: SizedBox(
|
|
||||||
height: 12,
|
height: 12,
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
child: _isHover
|
child: notifier.isHover
|
||||||
? FlowyTooltip(
|
? FlowyTooltip(
|
||||||
message: LocaleKeys.grid_checklist_addNew.tr(),
|
message: LocaleKeys.grid_checklist_addNew.tr(),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -190,11 +190,9 @@ class EditableCellBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class CellEditable {
|
abstract class CellEditable {
|
||||||
RequestFocusListener get requestFocus;
|
SingleListenerChangeNotifier get requestFocus;
|
||||||
|
|
||||||
CellContainerNotifier get cellContainerNotifier;
|
CellContainerNotifier get cellContainerNotifier;
|
||||||
|
|
||||||
// ValueNotifier<bool> get onCellEditing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef AccessoryBuilder = List<GridCellAccessoryBuilder> Function(
|
typedef AccessoryBuilder = List<GridCellAccessoryBuilder> Function(
|
||||||
@ -204,9 +202,6 @@ typedef AccessoryBuilder = List<GridCellAccessoryBuilder> Function(
|
|||||||
abstract class CellAccessory extends Widget {
|
abstract class CellAccessory extends Widget {
|
||||||
const CellAccessory({super.key});
|
const CellAccessory({super.key});
|
||||||
|
|
||||||
// The hover will show if the isHover's value is true
|
|
||||||
ValueNotifier<bool>? get onAccessoryHover;
|
|
||||||
|
|
||||||
AccessoryBuilder? get accessoryBuilder;
|
AccessoryBuilder? get accessoryBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,20 +212,11 @@ abstract class EditableCellWidget extends StatefulWidget
|
|||||||
@override
|
@override
|
||||||
final CellContainerNotifier cellContainerNotifier = CellContainerNotifier();
|
final CellContainerNotifier cellContainerNotifier = CellContainerNotifier();
|
||||||
|
|
||||||
// When the cell is focused, we assume that the accessory also be hovered.
|
|
||||||
@override
|
@override
|
||||||
ValueNotifier<bool> get onAccessoryHover => ValueNotifier(false);
|
AccessoryBuilder? get accessoryBuilder => null;
|
||||||
|
|
||||||
// @override
|
|
||||||
// final ValueNotifier<bool> onCellEditing = ValueNotifier<bool>(false);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<GridCellAccessoryBuilder> Function(
|
final requestFocus = SingleListenerChangeNotifier();
|
||||||
GridCellAccessoryBuildContext buildContext,
|
|
||||||
)? get accessoryBuilder => null;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final RequestFocusListener requestFocus = RequestFocusListener();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Map<CellKeyboardKey, CellKeyboardAction> shortcutHandlers = {};
|
final Map<CellKeyboardKey, CellKeyboardAction> shortcutHandlers = {};
|
||||||
@ -240,28 +226,25 @@ abstract class GridCellState<T extends EditableCellWidget> extends State<T> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
widget.requestFocus.addListener(onRequestFocus);
|
||||||
widget.requestFocus.setListener(requestBeginFocus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant T oldWidget) {
|
void didUpdateWidget(covariant T oldWidget) {
|
||||||
if (oldWidget != this) {
|
if (oldWidget != this) {
|
||||||
widget.requestFocus.setListener(requestBeginFocus);
|
widget.requestFocus.addListener(onRequestFocus);
|
||||||
}
|
}
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
widget.onAccessoryHover.dispose();
|
|
||||||
widget.requestFocus.removeAllListener();
|
|
||||||
widget.requestFocus.dispose();
|
widget.requestFocus.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subclass can override this method to request focus.
|
/// Subclass can override this method to request focus.
|
||||||
void requestBeginFocus();
|
void onRequestFocus();
|
||||||
|
|
||||||
String? onCopy() => null;
|
String? onCopy() => null;
|
||||||
}
|
}
|
||||||
@ -287,7 +270,7 @@ abstract class GridEditableTextCell<T extends EditableCellWidget>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void onRequestFocus() {
|
||||||
if (!focusNode.hasFocus && focusNode.canRequestFocus) {
|
if (!focusNode.hasFocus && focusNode.canRequestFocus) {
|
||||||
FocusScope.of(context).requestFocus(focusNode);
|
FocusScope.of(context).requestFocus(focusNode);
|
||||||
}
|
}
|
||||||
@ -304,28 +287,25 @@ abstract class GridEditableTextCell<T extends EditableCellWidget>
|
|||||||
Future<void> focusChanged() async {}
|
Future<void> focusChanged() async {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequestFocusListener extends ChangeNotifier {
|
class SingleListenerChangeNotifier extends ChangeNotifier {
|
||||||
VoidCallback? _listener;
|
VoidCallback? _listener;
|
||||||
|
|
||||||
void setListener(VoidCallback listener) {
|
@override
|
||||||
|
void addListener(VoidCallback listener) {
|
||||||
if (_listener != null) {
|
if (_listener != null) {
|
||||||
removeListener(_listener!);
|
removeListener(_listener!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_listener = listener;
|
_listener = listener;
|
||||||
addListener(listener);
|
super.addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeAllListener() {
|
@override
|
||||||
if (_listener != null) {
|
void dispose() {
|
||||||
removeListener(_listener!);
|
_listener = null;
|
||||||
_listener = null;
|
super.dispose();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify() {
|
void notify() => notifyListeners();
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SingleListenerFocusNode extends FocusNode {
|
class SingleListenerFocusNode extends FocusNode {
|
||||||
@ -374,7 +354,7 @@ class EditableCellSkinMap {
|
|||||||
FieldType.Checklist => checklistSkin != null,
|
FieldType.Checklist => checklistSkin != null,
|
||||||
FieldType.CreatedTime ||
|
FieldType.CreatedTime ||
|
||||||
FieldType.LastEditedTime =>
|
FieldType.LastEditedTime =>
|
||||||
throw timestampSkin != null,
|
timestampSkin != null,
|
||||||
FieldType.DateTime => dateSkin != null,
|
FieldType.DateTime => dateSkin != null,
|
||||||
FieldType.MultiSelect ||
|
FieldType.MultiSelect ||
|
||||||
FieldType.SingleSelect =>
|
FieldType.SingleSelect =>
|
||||||
|
@ -80,9 +80,7 @@ class _CheckboxCellState extends GridCellState<EditableCheckboxCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void onRequestFocus() => cellBloc.add(const CheckboxCellEvent.select());
|
||||||
cellBloc.add(const CheckboxCellEvent.select());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? onCopy() {
|
String? onCopy() {
|
||||||
|
@ -85,7 +85,7 @@ class GridChecklistCellState extends GridCellState<EditableChecklistCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void onRequestFocus() {
|
||||||
if (widget.skin is DesktopGridChecklistCellSkin) {
|
if (widget.skin is DesktopGridChecklistCellSkin) {
|
||||||
_popover.show();
|
_popover.show();
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ class _DateCellState extends GridCellState<EditableDateCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void onRequestFocus() {
|
||||||
_popover.show();
|
_popover.show();
|
||||||
widget.cellContainerNotifier.isFocus = true;
|
widget.cellContainerNotifier.isFocus = true;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ class _NumberCellState extends GridEditableTextCell<EditableNumberCell> {
|
|||||||
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
|
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void onRequestFocus() {
|
||||||
focusNode.requestFocus();
|
focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,5 +92,5 @@ class _SelectOptionCellState extends GridCellState<EditableSelectOptionCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() => _popover.show();
|
void onRequestFocus() => _popover.show();
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ class _TextCellState extends GridEditableTextCell<EditableTextCell> {
|
|||||||
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
|
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void onRequestFocus() {
|
||||||
focusNode.requestFocus();
|
focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class _TimestampCellState extends GridCellState<EditableTimestampCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void onRequestFocus() {
|
||||||
widget.cellContainerNotifier.isFocus = true;
|
widget.cellContainerNotifier.isFocus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ class _GridCellEnterRegion extends StatelessWidget {
|
|||||||
return Selector2<RegionStateNotifier, CellContainerNotifier, bool>(
|
return Selector2<RegionStateNotifier, CellContainerNotifier, bool>(
|
||||||
selector: (context, regionNotifier, cellNotifier) =>
|
selector: (context, regionNotifier, cellNotifier) =>
|
||||||
!cellNotifier.isFocus &&
|
!cellNotifier.isFocus &&
|
||||||
(cellNotifier.onEnter || regionNotifier.onEnter && isPrimary),
|
(cellNotifier.isHover || regionNotifier.onEnter && isPrimary),
|
||||||
builder: (context, showAccessory, _) {
|
builder: (context, showAccessory, _) {
|
||||||
final List<Widget> children = [child];
|
final List<Widget> children = [child];
|
||||||
|
|
||||||
@ -113,9 +113,9 @@ class _GridCellEnterRegion extends StatelessWidget {
|
|||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
onEnter: (p) =>
|
onEnter: (p) =>
|
||||||
CellContainerNotifier.of(context, listen: false).onEnter = true,
|
CellContainerNotifier.of(context, listen: false).isHover = true,
|
||||||
onExit: (p) =>
|
onExit: (p) =>
|
||||||
CellContainerNotifier.of(context, listen: false).onEnter = false,
|
CellContainerNotifier.of(context, listen: false).isHover = false,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.center,
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
@ -138,7 +138,7 @@ class CellContainerNotifier extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set onEnter(bool value) {
|
set isHover(bool value) {
|
||||||
if (_onEnter != value) {
|
if (_onEnter != value) {
|
||||||
_onEnter = value;
|
_onEnter = value;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -147,7 +147,7 @@ class CellContainerNotifier extends ChangeNotifier {
|
|||||||
|
|
||||||
bool get isFocus => _isFocus;
|
bool get isFocus => _isFocus;
|
||||||
|
|
||||||
bool get onEnter => _onEnter;
|
bool get isHover => _onEnter;
|
||||||
|
|
||||||
static CellContainerNotifier of(BuildContext context, {bool listen = true}) {
|
static CellContainerNotifier of(BuildContext context, {bool listen = true}) {
|
||||||
return Provider.of<CellContainerNotifier>(context, listen: listen);
|
return Provider.of<CellContainerNotifier>(context, listen: listen);
|
||||||
|
@ -189,8 +189,14 @@ class _PropertyCellState extends State<_PropertyCell> {
|
|||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
constraints: const BoxConstraints(minHeight: 30),
|
constraints: const BoxConstraints(minHeight: 30),
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
onEnter: (event) => _isFieldHover.value = true,
|
onEnter: (event) {
|
||||||
onExit: (event) => _isFieldHover.value = false,
|
_isFieldHover.value = true;
|
||||||
|
cell.cellContainerNotifier.isHover = true;
|
||||||
|
},
|
||||||
|
onExit: (event) {
|
||||||
|
_isFieldHover.value = false;
|
||||||
|
cell.cellContainerNotifier.isHover = false;
|
||||||
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
Loading…
Reference in New Issue
Block a user