Vincent Chan 2 лет назад
Родитель
Сommit
4fb760e44c

+ 8 - 10
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart

@@ -114,16 +114,14 @@ class FieldEditorPopOver extends StatelessWidget {
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return FlowyPopover(
     return FlowyPopover(
-        child: Container(
-      constraints: BoxConstraints.loose(const Size(280, 400)),
-      width: 280,
-      height: 400,
-      child: FieldEditor(
-          gridId: gridId,
-          fieldName: fieldName,
-          typeOptionLoader: typeOptionLoader,
-          key: key),
-    ));
+      builder: (BuildContext context) {
+        return FieldEditor(
+            gridId: gridId,
+            fieldName: fieldName,
+            typeOptionLoader: typeOptionLoader,
+            key: key);
+      },
+    );
   }
   }
 
 
   static show(
   static show(

+ 44 - 12
frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_popover.dart

@@ -1,21 +1,23 @@
+import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
+import 'package:flowy_infra_ui/style_widget/decoration.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:provider/provider.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
+import './flowy_popover_layout.dart';
 
 
 const _overlayContainerPadding = EdgeInsets.all(12);
 const _overlayContainerPadding = EdgeInsets.all(12);
 
 
-class FlowyPopover extends StatelessWidget {
-  final Widget child;
+class FlowyPopover extends StatefulWidget {
+  final Widget Function(BuildContext context) builder;
   final ShapeBorder? shape;
   final ShapeBorder? shape;
+  final EdgeInsets padding;
 
 
-  FlowyPopover({Key? key, required this.child, this.shape}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return SimpleDialog(
-      shape: shape ??
-          RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
-      children: [Container(padding: _overlayContainerPadding, child: child)],
-    );
-  }
+  FlowyPopover({
+    Key? key,
+    required this.builder,
+    this.shape,
+    this.padding = _overlayContainerPadding,
+  }) : super(key: key);
 
 
   static show(
   static show(
     BuildContext context, {
     BuildContext context, {
@@ -24,4 +26,34 @@ class FlowyPopover extends StatelessWidget {
     showDialog(
     showDialog(
         barrierColor: Colors.transparent, context: context, builder: builder);
         barrierColor: Colors.transparent, context: context, builder: builder);
   }
   }
+
+  @override
+  State<FlowyPopover> createState() => _FlowyPopoverState();
+}
+
+class _FlowyPopoverState extends State<FlowyPopover> {
+  final preRenderKey = GlobalKey();
+  Size? size;
+
+  @override
+  Widget build(BuildContext context) {
+    final theme =
+        context.watch<AppTheme?>() ?? AppTheme.fromType(ThemeType.light);
+    return Material(
+        type: MaterialType.transparency,
+        child: CustomSingleChildLayout(
+            delegate: PopoverLayoutDelegate(
+              anchorRect: const Rect.fromLTWH(0, 0, 280, 400),
+              anchorDirection: AnchorDirection.rightWithTopAligned,
+              overlapBehaviour: OverlapBehaviour.stretch,
+            ),
+            child: Container(
+              padding: widget.padding,
+              constraints: BoxConstraints.loose(const Size(280, 400)),
+              decoration: FlowyDecoration.decoration(
+                  theme.surface, theme.shadowColor.withOpacity(0.15)),
+              key: preRenderKey,
+              child: widget.builder(context),
+            )));
+  }
 }
 }

+ 251 - 0
frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_popover_layout.dart

@@ -0,0 +1,251 @@
+import 'dart:math' as math;
+import 'package:flutter/material.dart';
+import 'flowy_overlay.dart';
+
+class PopoverLayoutDelegate extends SingleChildLayoutDelegate {
+  PopoverLayoutDelegate({
+    required this.anchorRect,
+    required this.anchorDirection,
+    required this.overlapBehaviour,
+  });
+
+  final Rect anchorRect;
+  final AnchorDirection anchorDirection;
+  final OverlapBehaviour overlapBehaviour;
+
+  @override
+  bool shouldRelayout(PopoverLayoutDelegate oldDelegate) {
+    return anchorRect != oldDelegate.anchorRect ||
+        anchorDirection != oldDelegate.anchorDirection ||
+        overlapBehaviour != oldDelegate.overlapBehaviour;
+  }
+
+  @override
+  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
+    switch (overlapBehaviour) {
+      case OverlapBehaviour.none:
+        return constraints.loosen();
+      case OverlapBehaviour.stretch:
+        BoxConstraints childConstraints;
+        switch (anchorDirection) {
+          case AnchorDirection.topLeft:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.left,
+              anchorRect.top,
+            ));
+            break;
+          case AnchorDirection.topRight:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth - anchorRect.right,
+              anchorRect.top,
+            ));
+            break;
+          case AnchorDirection.bottomLeft:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.left,
+              constraints.maxHeight - anchorRect.bottom,
+            ));
+            break;
+          case AnchorDirection.bottomRight:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth - anchorRect.right,
+              constraints.maxHeight - anchorRect.bottom,
+            ));
+            break;
+          case AnchorDirection.center:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth,
+              constraints.maxHeight,
+            ));
+            break;
+          case AnchorDirection.topWithLeftAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth - anchorRect.left,
+              anchorRect.top,
+            ));
+            break;
+          case AnchorDirection.topWithCenterAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth,
+              anchorRect.top,
+            ));
+            break;
+          case AnchorDirection.topWithRightAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.right,
+              anchorRect.top,
+            ));
+            break;
+          case AnchorDirection.rightWithTopAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth - anchorRect.right,
+              constraints.maxHeight - anchorRect.top,
+            ));
+            break;
+          case AnchorDirection.rightWithCenterAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth - anchorRect.right,
+              constraints.maxHeight,
+            ));
+            break;
+          case AnchorDirection.rightWithBottomAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth - anchorRect.right,
+              anchorRect.bottom,
+            ));
+            break;
+          case AnchorDirection.bottomWithLeftAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.left,
+              constraints.maxHeight - anchorRect.bottom,
+            ));
+            break;
+          case AnchorDirection.bottomWithCenterAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth,
+              constraints.maxHeight - anchorRect.bottom,
+            ));
+            break;
+          case AnchorDirection.bottomWithRightAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.right,
+              constraints.maxHeight - anchorRect.bottom,
+            ));
+            break;
+          case AnchorDirection.leftWithTopAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.left,
+              constraints.maxHeight - anchorRect.top,
+            ));
+            break;
+          case AnchorDirection.leftWithCenterAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.left,
+              constraints.maxHeight,
+            ));
+            break;
+          case AnchorDirection.leftWithBottomAligned:
+            childConstraints = BoxConstraints.loose(Size(
+              anchorRect.left,
+              anchorRect.bottom,
+            ));
+            break;
+          case AnchorDirection.custom:
+            childConstraints = constraints.loosen();
+            break;
+          default:
+            throw UnimplementedError();
+        }
+        return childConstraints;
+    }
+  }
+
+  @override
+  Offset getPositionForChild(Size size, Size childSize) {
+    Offset position;
+    switch (anchorDirection) {
+      case AnchorDirection.topLeft:
+        position = Offset(
+          anchorRect.left - childSize.width,
+          anchorRect.top - childSize.height,
+        );
+        break;
+      case AnchorDirection.topRight:
+        position = Offset(
+          anchorRect.right,
+          anchorRect.top - childSize.height,
+        );
+        break;
+      case AnchorDirection.bottomLeft:
+        position = Offset(
+          anchorRect.left - childSize.width,
+          anchorRect.bottom,
+        );
+        break;
+      case AnchorDirection.bottomRight:
+        position = Offset(
+          anchorRect.right,
+          anchorRect.bottom,
+        );
+        break;
+      case AnchorDirection.center:
+        position = anchorRect.center;
+        break;
+      case AnchorDirection.topWithLeftAligned:
+        position = Offset(
+          anchorRect.left,
+          anchorRect.top - childSize.height,
+        );
+        break;
+      case AnchorDirection.topWithCenterAligned:
+        position = Offset(
+          anchorRect.left + anchorRect.width / 2.0 - childSize.width / 2.0,
+          anchorRect.top - childSize.height,
+        );
+        break;
+      case AnchorDirection.topWithRightAligned:
+        position = Offset(
+          anchorRect.right - childSize.width,
+          anchorRect.top - childSize.height,
+        );
+        break;
+      case AnchorDirection.rightWithTopAligned:
+        position = Offset(anchorRect.right, anchorRect.top);
+        break;
+      case AnchorDirection.rightWithCenterAligned:
+        position = Offset(
+          anchorRect.right,
+          anchorRect.top + anchorRect.height / 2.0 - childSize.height / 2.0,
+        );
+        break;
+      case AnchorDirection.rightWithBottomAligned:
+        position = Offset(
+          anchorRect.right,
+          anchorRect.bottom - childSize.height,
+        );
+        break;
+      case AnchorDirection.bottomWithLeftAligned:
+        position = Offset(
+          anchorRect.left,
+          anchorRect.bottom,
+        );
+        break;
+      case AnchorDirection.bottomWithCenterAligned:
+        position = Offset(
+          anchorRect.left + anchorRect.width / 2.0 - childSize.width / 2.0,
+          anchorRect.bottom,
+        );
+        break;
+      case AnchorDirection.bottomWithRightAligned:
+        position = Offset(
+          anchorRect.right - childSize.width,
+          anchorRect.bottom,
+        );
+        break;
+      case AnchorDirection.leftWithTopAligned:
+        position = Offset(
+          anchorRect.left - childSize.width,
+          anchorRect.top,
+        );
+        break;
+      case AnchorDirection.leftWithCenterAligned:
+        position = Offset(
+          anchorRect.left - childSize.width,
+          anchorRect.top + anchorRect.height / 2.0 - childSize.height / 2.0,
+        );
+        break;
+      case AnchorDirection.leftWithBottomAligned:
+        position = Offset(
+          anchorRect.left - childSize.width,
+          anchorRect.bottom - childSize.height,
+        );
+        break;
+      default:
+        throw UnimplementedError();
+    }
+    return Offset(
+      math.max(0.0, math.min(size.width - childSize.width, position.dx)),
+      math.max(0.0, math.min(size.height - childSize.height, position.dy)),
+    );
+  }
+}