diff --git a/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart b/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart index abeacbb38b..91768bd645 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart +++ b/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'; diff --git a/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui_web.dart b/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui_web.dart index abeacbb38b..91768bd645 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui_web.dart +++ b/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'; diff --git a/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/option_overlay.dart b/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/option_overlay.dart new file mode 100644 index 0000000000..8f2feaffb4 --- /dev/null +++ b/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 extends StatelessWidget { + const OptionOverlay({ + Key? key, + required this.items, + this.onHover, + this.onTap, + }) : super(key: key); + + final List items; + final IndexedValueCallback? onHover; + final IndexedValueCallback? onTap; + + static void showWithAnchor( + BuildContext context, { + required String identifier, + required List items, + IndexedValueCallback? onHover, + IndexedValueCallback? 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 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 = []; + 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.'); + } +}