Explorar el Código

refactor: use Popover in URL cell

Vincent Chan hace 2 años
padre
commit
6296367efc

+ 35 - 7
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_accessory.dart

@@ -20,14 +20,36 @@ class GridCellAccessoryBuildContext {
   });
 }
 
-abstract class GridCellAccessory implements Widget {
+class GridCellAccessoryBuilder {
+  final GlobalKey _key = GlobalKey();
+
+  final Widget Function(Key key) _builder;
+
+  GridCellAccessoryBuilder({required Widget Function(Key key) builder})
+      : _builder = builder;
+
+  Widget build() => _builder(_key);
+
+  void onTap() {
+    (_key.currentState as GridCellAccessoryState).onTap();
+  }
+
+  bool enable() {
+    if (_key.currentState == null) {
+      return true;
+    }
+    return (_key.currentState as GridCellAccessoryState).enable();
+  }
+}
+
+abstract class GridCellAccessoryState {
   void onTap();
 
   // The accessory will be hidden if enable() return false;
   bool enable() => true;
 }
 
-class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
+class PrimaryCellAccessory extends StatefulWidget {
   final VoidCallback onTapCallback;
   final bool isCellEditing;
   const PrimaryCellAccessory({
@@ -36,9 +58,15 @@ class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
     Key? key,
   }) : super(key: key);
 
+  @override
+  State<StatefulWidget> createState() => _PrimaryCellAccessoryState();
+}
+
+class _PrimaryCellAccessoryState extends State<PrimaryCellAccessory>
+    with GridCellAccessoryState {
   @override
   Widget build(BuildContext context) {
-    if (isCellEditing) {
+    if (widget.isCellEditing) {
       return const SizedBox();
     } else {
       final theme = context.watch<AppTheme>();
@@ -53,10 +81,10 @@ class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
   }
 
   @override
-  void onTap() => onTapCallback();
+  void onTap() => widget.onTapCallback();
 
   @override
-  bool enable() => !isCellEditing;
+  bool enable() => !widget.isCellEditing;
 }
 
 class AccessoryHover extends StatefulWidget {
@@ -170,7 +198,7 @@ class _Background extends StatelessWidget {
 }
 
 class CellAccessoryContainer extends StatelessWidget {
-  final List<GridCellAccessory> accessories;
+  final List<GridCellAccessoryBuilder> accessories;
   const CellAccessoryContainer({required this.accessories, Key? key})
       : super(key: key);
 
@@ -186,7 +214,7 @@ class CellAccessoryContainer extends StatelessWidget {
           width: 26,
           height: 26,
           padding: const EdgeInsets.all(3),
-          child: accessory,
+          child: accessory.build(),
         ),
       );
       return GestureDetector(

+ 3 - 3
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart

@@ -94,7 +94,7 @@ abstract class CellEditable {
   ValueNotifier<bool> get onCellEditing;
 }
 
-typedef AccessoryBuilder = List<GridCellAccessory> Function(
+typedef AccessoryBuilder = List<GridCellAccessoryBuilder> Function(
     GridCellAccessoryBuildContext buildContext);
 
 abstract class CellAccessory extends Widget {
@@ -125,8 +125,8 @@ abstract class GridCellWidget extends StatefulWidget
   final ValueNotifier<bool> onCellEditing = ValueNotifier<bool>(false);
 
   @override
-  List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)?
-      get accessoryBuilder => null;
+  List<GridCellAccessoryBuilder> Function(
+      GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null;
 
   @override
   final GridCellFocusListener beginFocus = GridCellFocusListener();

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_container.dart

@@ -80,7 +80,7 @@ class CellContainer extends StatelessWidget {
 
 class _GridCellEnterRegion extends StatelessWidget {
   final Widget child;
-  final List<GridCellAccessory> accessories;
+  final List<GridCellAccessoryBuilder> accessories;
   const _GridCellEnterRegion(
       {required this.child, required this.accessories, Key? key})
       : super(key: key);

+ 1 - 0
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart

@@ -64,6 +64,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
           return Popover(
             controller: _popover,
             offset: const Offset(0, 20),
+            direction: PopoverDirection.bottomWithLeftAligned,
             child: SizedBox.expand(
               child: GestureDetector(
                 behavior: HitTestBehavior.opaque,

+ 24 - 45
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart

@@ -6,56 +6,13 @@ import 'dart:async';
 
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
+class URLCellEditor extends StatefulWidget {
   final GridURLCellController cellController;
-  final VoidCallback completed;
-  const URLCellEditor(
-      {required this.cellController, required this.completed, Key? key})
+  const URLCellEditor({required this.cellController, Key? key})
       : super(key: key);
 
   @override
   State<URLCellEditor> createState() => _URLCellEditorState();
-
-  static void show(
-    BuildContext context,
-    GridURLCellController cellContext,
-    VoidCallback completed,
-  ) {
-    FlowyOverlay.of(context).remove(identifier());
-    final editor = URLCellEditor(
-      cellController: cellContext,
-      completed: completed,
-    );
-
-    //
-    FlowyOverlay.of(context).insertWithAnchor(
-      widget: OverlayContainer(
-        constraints: BoxConstraints.loose(const Size(300, 160)),
-        child: SizedBox(
-          width: 200,
-          child: Padding(padding: const EdgeInsets.all(6), child: editor),
-        ),
-      ),
-      identifier: URLCellEditor.identifier(),
-      anchorContext: context,
-      anchorDirection: AnchorDirection.bottomWithCenterAligned,
-      delegate: editor,
-    );
-  }
-
-  static String identifier() {
-    return (URLCellEditor).toString();
-  }
-
-  @override
-  bool asBarrier() {
-    return true;
-  }
-
-  @override
-  void didRemove() {
-    completed();
-  }
 }
 
 class _URLCellEditorState extends State<URLCellEditor> {
@@ -114,3 +71,25 @@ class _URLCellEditorState extends State<URLCellEditor> {
     }
   }
 }
+
+class URLEditorPopover extends StatelessWidget {
+  final GridURLCellController cellController;
+  const URLEditorPopover({required this.cellController, Key? key})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return OverlayContainer(
+      constraints: BoxConstraints.loose(const Size(300, 160)),
+      child: SizedBox(
+        width: 200,
+        child: Padding(
+          padding: const EdgeInsets.all(6),
+          child: URLCellEditor(
+            cellController: cellController,
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 77 - 21
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/plugins/grid/application/cell/url_cell_bloc.dart';
 import 'package:app_flowy/workspace/presentation/home/toast.dart';
+import 'package:appflowy_popover/popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -48,27 +49,37 @@ class GridURLCell extends GridCellWidget {
   @override
   GridCellState<GridURLCell> createState() => _GridURLCellState();
 
-  GridCellAccessory accessoryFromType(
+  GridCellAccessoryBuilder accessoryFromType(
       GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) {
     switch (ty) {
       case GridURLCellAccessoryType.edit:
         final cellController =
             cellControllerBuilder.build() as GridURLCellController;
-        return _EditURLAccessory(
+        return GridCellAccessoryBuilder(
+          builder: (Key key) => _EditURLAccessory(
+            key: key,
             cellContext: cellController,
-            anchorContext: buildContext.anchorContext);
+            anchorContext: buildContext.anchorContext,
+          ),
+        );
 
       case GridURLCellAccessoryType.copyURL:
         final cellContext =
             cellControllerBuilder.build() as GridURLCellController;
-        return _CopyURLAccessory(cellContext: cellContext);
+        return GridCellAccessoryBuilder(
+          builder: (Key key) => _CopyURLAccessory(
+            key: key,
+            cellContext: cellContext,
+          ),
+        );
     }
   }
 
   @override
-  List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)
+  List<GridCellAccessoryBuilder> Function(
+          GridCellAccessoryBuildContext buildContext)
       get accessoryBuilder => (buildContext) {
-            final List<GridCellAccessory> accessories = [];
+            final List<GridCellAccessoryBuilder> accessories = [];
             if (cellStyle != null) {
               accessories.addAll(cellStyle!.accessoryTypes.map((ty) {
                 return accessoryFromType(ty, buildContext);
@@ -86,6 +97,8 @@ class GridURLCell extends GridCellWidget {
 }
 
 class _GridURLCellState extends GridCellState<GridURLCell> {
+  final _popoverController = PopoverController();
+  GridURLCellController? _cellContext;
   late URLCellBloc _cellBloc;
 
   @override
@@ -116,14 +129,28 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
             ),
           );
 
-          return SizedBox.expand(
+          return Popover(
+            controller: _popoverController,
+            direction: PopoverDirection.bottomWithLeftAligned,
+            offset: const Offset(0, 20),
+            child: SizedBox.expand(
               child: GestureDetector(
-            child: Align(alignment: Alignment.centerLeft, child: richText),
-            onTap: () async {
-              final url = context.read<URLCellBloc>().state.url;
-              await _openUrlOrEdit(url);
+                child: Align(alignment: Alignment.centerLeft, child: richText),
+                onTap: () async {
+                  final url = context.read<URLCellBloc>().state.url;
+                  await _openUrlOrEdit(url);
+                },
+              ),
+            ),
+            popupBuilder: (BuildContext popoverContext) {
+              return URLEditorPopover(
+                cellController: _cellContext!,
+              );
+            },
+            onClose: () {
+              widget.onCellEditing.value = false;
             },
-          ));
+          );
         },
       ),
     );
@@ -140,12 +167,10 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
     if (url.isNotEmpty && await canLaunchUrl(uri)) {
       await launchUrl(uri);
     } else {
-      final cellContext =
+      _cellContext =
           widget.cellControllerBuilder.build() as GridURLCellController;
       widget.onCellEditing.value = true;
-      URLCellEditor.show(context, cellContext, () {
-        widget.onCellEditing.value = false;
-      });
+      _popoverController.show();
     }
   }
 
@@ -163,7 +188,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
   }
 }
 
-class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
+class _EditURLAccessory extends StatefulWidget {
   final GridURLCellController cellContext;
   final BuildContext anchorContext;
   const _EditURLAccessory({
@@ -172,24 +197,55 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
     Key? key,
   }) : super(key: key);
 
+  @override
+  State<StatefulWidget> createState() => _EditURLAccessoryState();
+}
+
+class _EditURLAccessoryState extends State<_EditURLAccessory>
+    with GridCellAccessoryState {
+  late PopoverController _popoverController;
+
+  @override
+  void initState() {
+    _popoverController = PopoverController();
+    super.initState();
+  }
+
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
-    return svgWidget("editor/edit", color: theme.iconColor);
+    return Popover(
+      controller: _popoverController,
+      direction: PopoverDirection.bottomWithLeftAligned,
+      triggerActions: PopoverTriggerActionFlags.click,
+      offset: const Offset(0, 20),
+      child: svgWidget("editor/edit", color: theme.iconColor),
+      popupBuilder: (BuildContext popoverContext) {
+        return URLEditorPopover(
+          cellController: widget.cellContext.clone(),
+        );
+      },
+    );
   }
 
   @override
   void onTap() {
-    URLCellEditor.show(anchorContext, cellContext, () {});
+    _popoverController.show();
   }
 }
 
-class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
+class _CopyURLAccessory extends StatefulWidget {
   final GridURLCellController cellContext;
   const _CopyURLAccessory({required this.cellContext, Key? key})
       : super(key: key);
 
   @override
+  State<StatefulWidget> createState() => _CopyURLAccessoryState();
+}
+
+class _CopyURLAccessoryState extends State<_CopyURLAccessory>
+    with GridCellAccessoryState {
+  @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
     return svgWidget("editor/copy", color: theme.iconColor);
@@ -198,7 +254,7 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
   @override
   void onTap() {
     final content =
-        cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
+        widget.cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
     Clipboard.setData(ClipboardData(text: content));
     showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
   }

+ 10 - 5
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart

@@ -194,12 +194,17 @@ class RowContent extends StatelessWidget {
               Provider.of<RegionStateNotifier>(context, listen: false),
           accessoryBuilder: (buildContext) {
             final builder = child.accessoryBuilder;
-            List<GridCellAccessory> accessories = [];
+            List<GridCellAccessoryBuilder> accessories = [];
             if (cellId.field.isPrimary) {
-              accessories.add(PrimaryCellAccessory(
-                onTapCallback: onExpand,
-                isCellEditing: buildContext.isCellEditing,
-              ));
+              accessories.add(
+                GridCellAccessoryBuilder(
+                  builder: (key) => PrimaryCellAccessory(
+                    key: key,
+                    onTapCallback: onExpand,
+                    isCellEditing: buildContext.isCellEditing,
+                  ),
+                ),
+              );
             }
 
             if (builder != null) {