mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[flutter]: config view action with FlowyOverlay
This commit is contained in:
parent
854eb69854
commit
3f9807ffff
@ -20,8 +20,6 @@ class AppWidgetTask extends LaunchTask {
|
||||
}
|
||||
}
|
||||
|
||||
final GlobalKey<FlowyOverlayState> _key = GlobalKey<FlowyOverlayState>();
|
||||
|
||||
class ApplicationWidget extends StatelessWidget {
|
||||
final Widget child;
|
||||
const ApplicationWidget({
|
||||
@ -38,7 +36,7 @@ class ApplicationWidget extends StatelessWidget {
|
||||
setWindowFrame(const Rect.fromLTRB(0, 0, launchWidth, launchWidth / ratio));
|
||||
|
||||
final theme = AppTheme.fromType(ThemeType.light);
|
||||
FlowyOverlayConfig config = FlowyOverlayConfig(barrierColor: theme.bg3.withOpacity(0.3));
|
||||
FlowyOverlayConfig config = FlowyOverlayConfig(barrierColor: Colors.transparent);
|
||||
return Provider.value(
|
||||
value: theme,
|
||||
child: MaterialApp(
|
||||
|
17
app_flowy/lib/workspace/domain/view_edit.dart
Normal file
17
app_flowy/lib/workspace/domain/view_edit.dart
Normal file
@ -0,0 +1,17 @@
|
||||
enum ViewAction {
|
||||
rename,
|
||||
delete,
|
||||
}
|
||||
|
||||
extension ViewActionExtension on ViewAction {
|
||||
String get name {
|
||||
switch (this) {
|
||||
case ViewAction.rename:
|
||||
return 'rename';
|
||||
case ViewAction.delete:
|
||||
return 'delete';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pbenum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
|
||||
@ -25,7 +24,7 @@ class HomeTopBar extends StatelessWidget {
|
||||
_renderNavigation(view),
|
||||
const Spacer(),
|
||||
_renderShareButton(),
|
||||
_renderMoreButton(),
|
||||
// _renderMoreButton(),
|
||||
],
|
||||
)
|
||||
.padding(
|
||||
@ -49,14 +48,6 @@ class HomeTopBar extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderMoreButton() {
|
||||
return ViewMoreButton(
|
||||
onPressed: () {
|
||||
debugPrint('show more');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderNavigation(HomeStackContext view) {
|
||||
return const FlowyNavigation();
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class MenuAppHeader extends StatelessWidget {
|
||||
fontSize: 12,
|
||||
),
|
||||
)),
|
||||
DisclosureButton(
|
||||
AddButton(
|
||||
onSelected: (viewType) {
|
||||
context.read<AppBloc>().add(AppEvent.createView("New view", "", viewType));
|
||||
},
|
||||
@ -70,9 +70,9 @@ class MenuAppHeader extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DisclosureButton extends StatelessWidget {
|
||||
class AddButton extends StatelessWidget {
|
||||
final Function(ViewType) onSelected;
|
||||
const DisclosureButton({
|
||||
const AddButton({
|
||||
Key? key,
|
||||
required this.onSelected,
|
||||
}) : super(key: key);
|
||||
@ -82,7 +82,7 @@ class DisclosureButton extends StatelessWidget {
|
||||
return FlowyIconButton(
|
||||
width: 16,
|
||||
onPressed: () {
|
||||
DisclosureButtonActionList(
|
||||
ActionList(
|
||||
anchorContext: context,
|
||||
onSelected: onSelected,
|
||||
).show(context);
|
||||
@ -92,12 +92,12 @@ class DisclosureButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DisclosureButtonActionList {
|
||||
class ActionList {
|
||||
final Function(ViewType) onSelected;
|
||||
final BuildContext anchorContext;
|
||||
final String _identifier = 'DisclosureButtonActionList';
|
||||
|
||||
const DisclosureButtonActionList({required this.anchorContext, required this.onSelected});
|
||||
const ActionList({required this.anchorContext, required this.onSelected});
|
||||
|
||||
void show(BuildContext buildContext) {
|
||||
final items = ViewType.values.where((element) => element != ViewType.Blank).map((ty) {
|
||||
@ -134,19 +134,17 @@ class CreateItem extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final config = HoverDisplayConfig(hoverColor: theme.bg3);
|
||||
final config = HoverDisplayConfig(hoverColor: theme.hover);
|
||||
|
||||
return FlowyHover(
|
||||
config: config,
|
||||
builder: (context, onHover) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onSelected(viewType);
|
||||
},
|
||||
onTap: () => onSelected(viewType),
|
||||
child: FlowyText.medium(
|
||||
viewType.name,
|
||||
fontSize: 12,
|
||||
).padding(horizontal: 10, vertical: 10),
|
||||
).padding(horizontal: 10, vertical: 6),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -1,31 +1,47 @@
|
||||
import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
|
||||
import 'package:dartz/dartz.dart' as dartz;
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/workspace/domain/image.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import 'package:app_flowy/workspace/domain/image.dart';
|
||||
import 'package:app_flowy/workspace/domain/view_edit.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
|
||||
|
||||
class ViewWidgetContext {
|
||||
final View view;
|
||||
|
||||
ViewWidgetContext(this.view);
|
||||
|
||||
Key valueKey() => ValueKey("${view.id}${view.version}");
|
||||
}
|
||||
|
||||
typedef OpenViewCallback = void Function(View);
|
||||
|
||||
class ViewSectionItem extends StatelessWidget {
|
||||
// ignore: must_be_immutable
|
||||
class ViewSectionItem extends StatefulWidget {
|
||||
final ViewWidgetContext viewCtx;
|
||||
final bool isSelected;
|
||||
final OpenViewCallback onOpen;
|
||||
ViewSectionItem({Key? key, required this.viewCtx, required this.onOpen, required this.isSelected})
|
||||
: super(key: viewCtx.valueKey());
|
||||
|
||||
ViewSectionItem({
|
||||
Key? key,
|
||||
required this.viewCtx,
|
||||
required this.isSelected,
|
||||
required this.onOpen,
|
||||
}) : super(key: viewCtx.valueKey());
|
||||
|
||||
@override
|
||||
State<ViewSectionItem> createState() => _ViewSectionItemState();
|
||||
}
|
||||
|
||||
class _ViewSectionItemState extends State<ViewSectionItem> {
|
||||
bool isOnSelected = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -36,6 +52,7 @@ class ViewSectionItem extends StatelessWidget {
|
||||
child: FlowyHover(
|
||||
config: config,
|
||||
builder: (context, onHover) => _render(context, onHover, config),
|
||||
isOnSelected: () => isOnSelected || widget.isSelected,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -45,25 +62,36 @@ class ViewSectionItem extends StatelessWidget {
|
||||
SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: svgForViewType(viewCtx.view.viewType),
|
||||
child: svgForViewType(widget.viewCtx.view.viewType),
|
||||
),
|
||||
const HSpace(6),
|
||||
FlowyText.regular(
|
||||
viewCtx.view.name,
|
||||
widget.viewCtx.view.name,
|
||||
fontSize: 12,
|
||||
),
|
||||
];
|
||||
|
||||
if (onHover) {
|
||||
if (onHover || isOnSelected) {
|
||||
children.add(const Spacer());
|
||||
children.add(ViewMoreButton(
|
||||
onPressed: () {
|
||||
debugPrint('show view setting');
|
||||
children.add(ViewDisclosureButton(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isOnSelected = true;
|
||||
});
|
||||
},
|
||||
onSelected: (selected) {
|
||||
selected.fold(() => null, (action) {
|
||||
debugPrint('$action.name');
|
||||
});
|
||||
|
||||
setState(() {
|
||||
isOnSelected = false;
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Widget widget = Container(
|
||||
return Container(
|
||||
child: Row(children: children).padding(
|
||||
left: MenuAppSizes.expandedPadding,
|
||||
right: MenuAppSizes.expandedIconPadding,
|
||||
@ -71,15 +99,100 @@ class ViewSectionItem extends StatelessWidget {
|
||||
height: 24,
|
||||
alignment: Alignment.centerLeft,
|
||||
);
|
||||
|
||||
if (isSelected) {
|
||||
widget = FlowyHoverBackground(child: widget, config: config);
|
||||
}
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
Function() _openView(BuildContext context) {
|
||||
return () => onOpen(viewCtx.view);
|
||||
return () => widget.onOpen(widget.viewCtx.view);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewDisclosureButton extends StatelessWidget {
|
||||
final Function(dartz.Option<ViewAction>) onSelected;
|
||||
final Function() onTap;
|
||||
const ViewDisclosureButton({
|
||||
Key? key,
|
||||
required this.onSelected,
|
||||
required this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyIconButton(
|
||||
width: 16,
|
||||
onPressed: () {
|
||||
onTap();
|
||||
ViewActionList(
|
||||
anchorContext: context,
|
||||
onSelected: onSelected,
|
||||
).show(context);
|
||||
},
|
||||
icon: svg("editor/details"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewActionList implements FlowyOverlayDelegate {
|
||||
final Function(dartz.Option<ViewAction>) onSelected;
|
||||
final BuildContext anchorContext;
|
||||
final String _identifier = 'ViewActionList';
|
||||
|
||||
const ViewActionList({required this.anchorContext, required this.onSelected});
|
||||
|
||||
void show(BuildContext buildContext) {
|
||||
final items = ViewAction.values.map((action) {
|
||||
return ActionItem(
|
||||
action: action,
|
||||
onSelected: (action) {
|
||||
FlowyOverlay.of(buildContext).remove(_identifier);
|
||||
onSelected(dartz.some(action));
|
||||
});
|
||||
}).toList();
|
||||
|
||||
// TODO: make sure the delegate of this wouldn't cause retain cycle
|
||||
ListOverlay.showWithAnchor(
|
||||
buildContext,
|
||||
identifier: _identifier,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) => items[index],
|
||||
anchorContext: anchorContext,
|
||||
anchorDirection: AnchorDirection.bottomRight,
|
||||
maxWidth: 120,
|
||||
maxHeight: 80,
|
||||
delegate: this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didRemove() {
|
||||
onSelected(dartz.none());
|
||||
}
|
||||
}
|
||||
|
||||
class ActionItem extends StatelessWidget {
|
||||
final ViewAction action;
|
||||
final Function(ViewAction) onSelected;
|
||||
const ActionItem({
|
||||
Key? key,
|
||||
required this.action,
|
||||
required this.onSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final config = HoverDisplayConfig(hoverColor: theme.hover);
|
||||
|
||||
return FlowyHover(
|
||||
config: config,
|
||||
builder: (context, onHover) {
|
||||
return GestureDetector(
|
||||
onTap: () => onSelected(action),
|
||||
child: FlowyText.medium(
|
||||
action.name,
|
||||
fontSize: 12,
|
||||
).padding(horizontal: 10, vertical: 6),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class ViewSection extends StatelessWidget {
|
||||
viewCtx: viewCtx,
|
||||
isSelected: _isViewSelected(context, view.id),
|
||||
onOpen: (view) {
|
||||
Log.debug("Open view: $view");
|
||||
Log.debug("Open: $view");
|
||||
context.read<ViewSectionNotifier>().setSelectedView(view);
|
||||
final stackView = stackCtxFromView(viewCtx.view);
|
||||
getIt<HomeStackManager>().setStack(stackView);
|
||||
|
@ -30,12 +30,14 @@ class ListOverlay extends StatelessWidget {
|
||||
BoxShadow(color: Colors.black.withOpacity(0.1), spreadRadius: 1, blurRadius: 20.0),
|
||||
],
|
||||
),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemBuilder: itemBuilder,
|
||||
itemCount: itemCount,
|
||||
controller: controller,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemBuilder: itemBuilder,
|
||||
itemCount: itemCount,
|
||||
controller: controller,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -4,14 +4,18 @@ import 'package:flowy_infra/time/duration.dart';
|
||||
|
||||
typedef HoverBuilder = Widget Function(BuildContext context, bool onHover);
|
||||
|
||||
typedef IsOnSelected = bool Function();
|
||||
|
||||
class FlowyHover extends StatefulWidget {
|
||||
final HoverDisplayConfig config;
|
||||
final HoverBuilder builder;
|
||||
final IsOnSelected? isOnSelected;
|
||||
|
||||
const FlowyHover({
|
||||
Key? key,
|
||||
required this.builder,
|
||||
required this.config,
|
||||
this.isOnSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -34,7 +38,13 @@ class _FlowyHoverState extends State<FlowyHover> {
|
||||
void setOnHover(bool value) => setState(() => _onHover = value);
|
||||
|
||||
Widget render() {
|
||||
if (_onHover) {
|
||||
var showHover = _onHover;
|
||||
|
||||
if (showHover == false && widget.isOnSelected != null) {
|
||||
showHover = widget.isOnSelected!();
|
||||
}
|
||||
|
||||
if (showHover) {
|
||||
return FlowyHoverBackground(
|
||||
config: widget.config,
|
||||
child: widget.builder(context, _onHover),
|
||||
|
@ -47,20 +47,3 @@ class FlowyDropdownButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewMoreButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
const ViewMoreButton({
|
||||
Key? key,
|
||||
this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyIconButton(
|
||||
width: 16,
|
||||
onPressed: onPressed,
|
||||
icon: svg("editor/details"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user