|
@@ -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.');
|
|
|
+ }
|
|
|
+}
|