Skip to content

Commit f61bebe

Browse files
committed
chore: hide accessory when cell is editing
1 parent fbf7c9c commit f61bebe

File tree

7 files changed

+225
-179
lines changed

7 files changed

+225
-179
lines changed

frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flowy_infra/image.dart';
12
import 'package:flowy_infra_ui/style_widget/hover.dart';
23
import 'package:flutter/widgets.dart';
34
import 'package:flowy_infra/theme.dart';
@@ -8,21 +9,54 @@ import 'package:styled_widget/styled_widget.dart';
89

910
class GridCellAccessoryBuildContext {
1011
final BuildContext anchorContext;
12+
final bool isCellEditing;
1113

12-
GridCellAccessoryBuildContext({required this.anchorContext});
14+
GridCellAccessoryBuildContext({
15+
required this.anchorContext,
16+
required this.isCellEditing,
17+
});
1318
}
1419

1520
abstract class GridCellAccessory implements Widget {
1621
void onTap();
22+
23+
// The accessory will be hidden if enable() return false;
24+
bool enable() => true;
25+
}
26+
27+
class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
28+
final VoidCallback onTapCallback;
29+
final bool isCellEditing;
30+
const PrimaryCellAccessory({
31+
required this.onTapCallback,
32+
required this.isCellEditing,
33+
Key? key,
34+
}) : super(key: key);
35+
36+
@override
37+
Widget build(BuildContext context) {
38+
if (isCellEditing) {
39+
return const SizedBox();
40+
} else {
41+
final theme = context.watch<AppTheme>();
42+
return svgWidget("grid/expander", color: theme.main1);
43+
}
44+
}
45+
46+
@override
47+
void onTap() => onTapCallback();
48+
49+
@override
50+
bool enable() => !isCellEditing;
1751
}
1852

1953
typedef AccessoryBuilder = List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext);
2054

2155
abstract class CellAccessory extends Widget {
2256
const CellAccessory({Key? key}) : super(key: key);
2357

24-
// The hover will show if the onFocus's value is true
25-
ValueNotifier<bool>? get isFocus;
58+
// The hover will show if the isHover's value is true
59+
ValueNotifier<bool>? get onAccessoryHover;
2660

2761
AccessoryBuilder? get accessoryBuilder;
2862
}
@@ -47,8 +81,8 @@ class _AccessoryHoverState extends State<AccessoryHover> {
4781
@override
4882
void initState() {
4983
_hoverState = AccessoryHoverState();
50-
_listenerFn = () => _hoverState.isFocus = widget.child.isFocus?.value ?? false;
51-
widget.child.isFocus?.addListener(_listenerFn!);
84+
_listenerFn = () => _hoverState.onHover = widget.child.onAccessoryHover?.value ?? false;
85+
widget.child.onAccessoryHover?.addListener(_listenerFn!);
5286

5387
super.initState();
5488
}
@@ -58,7 +92,7 @@ class _AccessoryHoverState extends State<AccessoryHover> {
5892
_hoverState.dispose();
5993

6094
if (_listenerFn != null) {
61-
widget.child.isFocus?.removeListener(_listenerFn!);
95+
widget.child.onAccessoryHover?.removeListener(_listenerFn!);
6296
_listenerFn = null;
6397
}
6498
super.dispose();
@@ -73,11 +107,14 @@ class _AccessoryHoverState extends State<AccessoryHover> {
73107

74108
final accessoryBuilder = widget.child.accessoryBuilder;
75109
if (accessoryBuilder != null) {
76-
final accessories = accessoryBuilder((GridCellAccessoryBuildContext(anchorContext: context)));
110+
final accessories = accessoryBuilder((GridCellAccessoryBuildContext(
111+
anchorContext: context,
112+
isCellEditing: false,
113+
)));
77114
children.add(
78115
Padding(
79116
padding: const EdgeInsets.only(right: 6),
80-
child: AccessoryContainer(accessories: accessories),
117+
child: CellAccessoryContainer(accessories: accessories),
81118
).positioned(right: 0),
82119
);
83120
}
@@ -101,7 +138,6 @@ class _AccessoryHoverState extends State<AccessoryHover> {
101138

102139
class AccessoryHoverState extends ChangeNotifier {
103140
bool _onHover = false;
104-
bool _isFocus = false;
105141

106142
set onHover(bool value) {
107143
if (_onHover != value) {
@@ -111,15 +147,6 @@ class AccessoryHoverState extends ChangeNotifier {
111147
}
112148

113149
bool get onHover => _onHover;
114-
115-
set isFocus(bool value) {
116-
if (_isFocus != value) {
117-
_isFocus = value;
118-
notifyListeners();
119-
}
120-
}
121-
122-
bool get isFocus => _isFocus;
123150
}
124151

125152
class _Background extends StatelessWidget {
@@ -130,7 +157,7 @@ class _Background extends StatelessWidget {
130157
final theme = context.watch<AppTheme>();
131158
return Consumer<AccessoryHoverState>(
132159
builder: (context, state, child) {
133-
if (state.onHover || state.isFocus) {
160+
if (state.onHover) {
134161
return FlowyHoverContainer(
135162
style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6),
136163
);
@@ -142,14 +169,14 @@ class _Background extends StatelessWidget {
142169
}
143170
}
144171

145-
class AccessoryContainer extends StatelessWidget {
172+
class CellAccessoryContainer extends StatelessWidget {
146173
final List<GridCellAccessory> accessories;
147-
const AccessoryContainer({required this.accessories, Key? key}) : super(key: key);
174+
const CellAccessoryContainer({required this.accessories, Key? key}) : super(key: key);
148175

149176
@override
150177
Widget build(BuildContext context) {
151178
final theme = context.watch<AppTheme>();
152-
final children = accessories.map((accessory) {
179+
final children = accessories.where((accessory) => accessory.enable()).map((accessory) {
153180
final hover = FlowyHover(
154181
style: HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface),
155182
builder: (_, onHover) => Container(

frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart

Lines changed: 24 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_serv
22
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
33
import 'package:flutter/services.dart';
44
import 'package:flutter/widgets.dart';
5-
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart';
6-
import 'package:flowy_infra/theme.dart';
75
import 'package:flutter/material.dart';
8-
import 'package:provider/provider.dart';
9-
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
10-
import 'package:styled_widget/styled_widget.dart';
116
import 'cell_accessory.dart';
127
import 'cell_shortcuts.dart';
138
import 'checkbox_cell.dart';
@@ -50,11 +45,30 @@ class BlankCell extends StatelessWidget {
5045
}
5146
}
5247

53-
abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellFocustable, CellShortcuts {
54-
GridCellWidget({Key? key}) : super(key: key);
48+
abstract class CellEditable {
49+
GridCellFocusListener get beginFocus;
50+
51+
ValueNotifier<bool> get onCellFocus;
52+
53+
ValueNotifier<bool> get onCellEditing;
54+
}
55+
56+
abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellEditable, CellShortcuts {
57+
GridCellWidget({Key? key}) : super(key: key) {
58+
onCellEditing.addListener(() {
59+
onCellFocus.value = onCellEditing.value;
60+
});
61+
}
62+
63+
@override
64+
final ValueNotifier<bool> onCellFocus = ValueNotifier<bool>(false);
65+
66+
// When the cell is focused, we assume that the accessory alse be hovered.
67+
@override
68+
ValueNotifier<bool> get onAccessoryHover => onCellFocus;
5569

5670
@override
57-
final ValueNotifier<bool> isFocus = ValueNotifier<bool>(false);
71+
final ValueNotifier<bool> onCellEditing = ValueNotifier<bool>(false);
5872

5973
@override
6074
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null;
@@ -137,9 +151,9 @@ abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCell
137151
}
138152

139153
void _listenOnFocusNodeChanged() {
140-
widget.isFocus.value = focusNode.hasFocus;
154+
widget.onCellEditing.value = focusNode.hasFocus;
141155
focusNode.setListener(() {
142-
widget.isFocus.value = focusNode.hasFocus;
156+
widget.onCellEditing.value = focusNode.hasFocus;
143157
focusChanged();
144158
});
145159
}
@@ -190,121 +204,3 @@ class SingleListenrFocusNode extends FocusNode {
190204
}
191205
}
192206
}
193-
194-
class CellStateNotifier extends ChangeNotifier {
195-
bool _isFocus = false;
196-
bool _onEnter = false;
197-
198-
set isFocus(bool value) {
199-
if (_isFocus != value) {
200-
_isFocus = value;
201-
notifyListeners();
202-
}
203-
}
204-
205-
set onEnter(bool value) {
206-
if (_onEnter != value) {
207-
_onEnter = value;
208-
notifyListeners();
209-
}
210-
}
211-
212-
bool get isFocus => _isFocus;
213-
214-
bool get onEnter => _onEnter;
215-
}
216-
217-
abstract class CellFocustable {
218-
GridCellFocusListener get beginFocus;
219-
}
220-
221-
class CellContainer extends StatelessWidget {
222-
final GridCellWidget child;
223-
final AccessoryBuilder? accessoryBuilder;
224-
final double width;
225-
final RegionStateNotifier rowStateNotifier;
226-
const CellContainer({
227-
Key? key,
228-
required this.child,
229-
required this.width,
230-
required this.rowStateNotifier,
231-
this.accessoryBuilder,
232-
}) : super(key: key);
233-
234-
@override
235-
Widget build(BuildContext context) {
236-
return ChangeNotifierProxyProvider<RegionStateNotifier, CellStateNotifier>(
237-
create: (_) => CellStateNotifier(),
238-
update: (_, row, cell) => cell!..onEnter = row.onEnter,
239-
child: Selector<CellStateNotifier, bool>(
240-
selector: (context, notifier) => notifier.isFocus,
241-
builder: (context, isFocus, _) {
242-
Widget container = Center(child: GridCellShortcuts(child: child));
243-
child.isFocus.addListener(() {
244-
Provider.of<CellStateNotifier>(context, listen: false).isFocus = child.isFocus.value;
245-
});
246-
247-
if (accessoryBuilder != null) {
248-
final buildContext = GridCellAccessoryBuildContext(anchorContext: context);
249-
final accessories = accessoryBuilder!(buildContext);
250-
if (accessories.isNotEmpty) {
251-
container = CellEnterRegion(child: container, accessories: accessories);
252-
}
253-
}
254-
255-
return GestureDetector(
256-
behavior: HitTestBehavior.translucent,
257-
onTap: () => child.beginFocus.notify(),
258-
child: Container(
259-
constraints: BoxConstraints(maxWidth: width, minHeight: 46),
260-
decoration: _makeBoxDecoration(context, isFocus),
261-
padding: GridSize.cellContentInsets,
262-
child: container,
263-
),
264-
);
265-
},
266-
),
267-
);
268-
}
269-
270-
BoxDecoration _makeBoxDecoration(BuildContext context, bool isFocus) {
271-
final theme = context.watch<AppTheme>();
272-
if (isFocus) {
273-
final borderSide = BorderSide(color: theme.main1, width: 1.0);
274-
return BoxDecoration(border: Border.fromBorderSide(borderSide));
275-
} else {
276-
final borderSide = BorderSide(color: theme.shader5, width: 1.0);
277-
return BoxDecoration(border: Border(right: borderSide, bottom: borderSide));
278-
}
279-
}
280-
}
281-
282-
class CellEnterRegion extends StatelessWidget {
283-
final Widget child;
284-
final List<GridCellAccessory> accessories;
285-
const CellEnterRegion({required this.child, required this.accessories, Key? key}) : super(key: key);
286-
287-
@override
288-
Widget build(BuildContext context) {
289-
return Selector<CellStateNotifier, bool>(
290-
selector: (context, notifier) => notifier.onEnter,
291-
builder: (context, onEnter, _) {
292-
List<Widget> children = [child];
293-
if (onEnter) {
294-
children.add(AccessoryContainer(accessories: accessories).positioned(right: 0));
295-
}
296-
297-
return MouseRegion(
298-
cursor: SystemMouseCursors.click,
299-
onEnter: (p) => Provider.of<CellStateNotifier>(context, listen: false).onEnter = true,
300-
onExit: (p) => Provider.of<CellStateNotifier>(context, listen: false).onEnter = false,
301-
child: Stack(
302-
alignment: AlignmentDirectional.center,
303-
fit: StackFit.expand,
304-
children: children,
305-
),
306-
);
307-
},
308-
);
309-
}
310-
}

0 commit comments

Comments
 (0)