فهرست منبع

[infra_ui][overlay] Implement option overlay

Jaylen Bian 3 سال پیش
والد
کامیت
82e6856f75

+ 2 - 0
app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart

@@ -6,3 +6,5 @@ export 'src/keyboard/keyboard_visibility_detector.dart';
 
 // Overlay
 export 'src/flowy_overlay/flowy_overlay.dart';
+export 'src/flowy_overlay/list_overlay.dart';
+export 'src/flowy_overlay/option_overlay.dart';

+ 2 - 0
app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui_web.dart

@@ -6,3 +6,5 @@ export 'src/keyboard/keyboard_visibility_detector.dart';
 
 // Overlay
 export 'src/flowy_overlay/flowy_overlay.dart';
+export 'src/flowy_overlay/list_overlay.dart';
+export 'src/flowy_overlay/option_overlay.dart';

+ 97 - 0
app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/option_overlay.dart

@@ -0,0 +1,97 @@
+import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
+import 'package:flutter/material.dart';
+
+class OptionItem {
+  const OptionItem(this.icon, this.title);
+
+  final Icon? icon;
+  final String title;
+}
+
+class OptionOverlay<T> extends StatelessWidget {
+  const OptionOverlay({
+    Key? key,
+    required this.items,
+    this.onHover,
+    this.onTap,
+  }) : super(key: key);
+
+  final List<T> items;
+  final IndexedValueCallback<T>? onHover;
+  final IndexedValueCallback<T>? onTap;
+
+  static void showWithAnchor<T>(
+    BuildContext context, {
+    required String identifier,
+    required List<T> items,
+    IndexedValueCallback<T>? onHover,
+    IndexedValueCallback<T>? onTap,
+    required BuildContext anchorContext,
+    AnchorDirection? anchorDirection,
+    FlowyOverlayDelegate? delegate,
+    OverlapBehaviour? overlapBehaviour,
+  }) {
+    FlowyOverlay.of(context).insertWithAnchor(
+      widget: OptionOverlay(
+        items: items,
+        onHover: onHover,
+        onTap: onTap,
+      ),
+      identifier: identifier,
+      anchorContext: anchorContext,
+      anchorDirection: anchorDirection,
+      delegate: delegate,
+      overlapBehaviour: overlapBehaviour,
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final List<_OptionListItem> listItems = items.map((e) => _OptionListItem(e)).toList();
+    return ListOverlay(
+      itemBuilder: (context, index) {
+        return MouseRegion(
+          cursor: SystemMouseCursors.click,
+          onHover: onHover != null ? (_) => onHover!(items[index], index) : null,
+          child: GestureDetector(
+            onTap: onTap != null ? () => onTap!(items[index], index) : null,
+            child: listItems[index],
+          ),
+        );
+      },
+      itemCount: listItems.length,
+    );
+  }
+}
+
+class _OptionListItem<T> extends StatelessWidget {
+  const _OptionListItem(
+    this.value, {
+    Key? key,
+  }) : super(key: key);
+
+  final T value;
+
+  @override
+  Widget build(BuildContext context) {
+    if (T == String || T == OptionItem) {
+      var children = <Widget>[];
+      if (value is String) {
+        children = [
+          Text(value as String),
+        ];
+      } else if (value is OptionItem) {
+        final optionItem = value as OptionItem;
+        children = [
+          if (optionItem.icon != null) optionItem.icon!,
+          Text(optionItem.title),
+        ];
+      }
+      return Column(
+        mainAxisSize: MainAxisSize.min,
+        children: children,
+      );
+    }
+    throw UnimplementedError('The type $T is not supported by option list.');
+  }
+}