mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor: use Popover in URL cell
This commit is contained in:
parent
a43259bddb
commit
6296367efc
@ -20,14 +20,36 @@ class GridCellAccessoryBuildContext {
|
||||
});
|
||||
}
|
||||
|
||||
abstract class GridCellAccessory implements Widget {
|
||||
class GridCellAccessoryBuilder {
|
||||
final GlobalKey _key = GlobalKey();
|
||||
|
||||
final Widget Function(Key key) _builder;
|
||||
|
||||
GridCellAccessoryBuilder({required Widget Function(Key key) builder})
|
||||
: _builder = builder;
|
||||
|
||||
Widget build() => _builder(_key);
|
||||
|
||||
void onTap() {
|
||||
(_key.currentState as GridCellAccessoryState).onTap();
|
||||
}
|
||||
|
||||
bool enable() {
|
||||
if (_key.currentState == null) {
|
||||
return true;
|
||||
}
|
||||
return (_key.currentState as GridCellAccessoryState).enable();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GridCellAccessoryState {
|
||||
void onTap();
|
||||
|
||||
// The accessory will be hidden if enable() return false;
|
||||
bool enable() => true;
|
||||
}
|
||||
|
||||
class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
|
||||
class PrimaryCellAccessory extends StatefulWidget {
|
||||
final VoidCallback onTapCallback;
|
||||
final bool isCellEditing;
|
||||
const PrimaryCellAccessory({
|
||||
@ -36,9 +58,15 @@ class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _PrimaryCellAccessoryState();
|
||||
}
|
||||
|
||||
class _PrimaryCellAccessoryState extends State<PrimaryCellAccessory>
|
||||
with GridCellAccessoryState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isCellEditing) {
|
||||
if (widget.isCellEditing) {
|
||||
return const SizedBox();
|
||||
} else {
|
||||
final theme = context.watch<AppTheme>();
|
||||
@ -53,10 +81,10 @@ class PrimaryCellAccessory extends StatelessWidget with GridCellAccessory {
|
||||
}
|
||||
|
||||
@override
|
||||
void onTap() => onTapCallback();
|
||||
void onTap() => widget.onTapCallback();
|
||||
|
||||
@override
|
||||
bool enable() => !isCellEditing;
|
||||
bool enable() => !widget.isCellEditing;
|
||||
}
|
||||
|
||||
class AccessoryHover extends StatefulWidget {
|
||||
@ -170,7 +198,7 @@ class _Background extends StatelessWidget {
|
||||
}
|
||||
|
||||
class CellAccessoryContainer extends StatelessWidget {
|
||||
final List<GridCellAccessory> accessories;
|
||||
final List<GridCellAccessoryBuilder> accessories;
|
||||
const CellAccessoryContainer({required this.accessories, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@ -186,7 +214,7 @@ class CellAccessoryContainer extends StatelessWidget {
|
||||
width: 26,
|
||||
height: 26,
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: accessory,
|
||||
child: accessory.build(),
|
||||
),
|
||||
);
|
||||
return GestureDetector(
|
||||
|
@ -94,7 +94,7 @@ abstract class CellEditable {
|
||||
ValueNotifier<bool> get onCellEditing;
|
||||
}
|
||||
|
||||
typedef AccessoryBuilder = List<GridCellAccessory> Function(
|
||||
typedef AccessoryBuilder = List<GridCellAccessoryBuilder> Function(
|
||||
GridCellAccessoryBuildContext buildContext);
|
||||
|
||||
abstract class CellAccessory extends Widget {
|
||||
@ -125,8 +125,8 @@ abstract class GridCellWidget extends StatefulWidget
|
||||
final ValueNotifier<bool> onCellEditing = ValueNotifier<bool>(false);
|
||||
|
||||
@override
|
||||
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)?
|
||||
get accessoryBuilder => null;
|
||||
List<GridCellAccessoryBuilder> Function(
|
||||
GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null;
|
||||
|
||||
@override
|
||||
final GridCellFocusListener beginFocus = GridCellFocusListener();
|
||||
|
@ -80,7 +80,7 @@ class CellContainer extends StatelessWidget {
|
||||
|
||||
class _GridCellEnterRegion extends StatelessWidget {
|
||||
final Widget child;
|
||||
final List<GridCellAccessory> accessories;
|
||||
final List<GridCellAccessoryBuilder> accessories;
|
||||
const _GridCellEnterRegion(
|
||||
{required this.child, required this.accessories, Key? key})
|
||||
: super(key: key);
|
||||
|
@ -64,6 +64,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
|
||||
return Popover(
|
||||
controller: _popover,
|
||||
offset: const Offset(0, 20),
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
child: SizedBox.expand(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
|
@ -6,56 +6,13 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
||||
class URLCellEditor extends StatefulWidget {
|
||||
final GridURLCellController cellController;
|
||||
final VoidCallback completed;
|
||||
const URLCellEditor(
|
||||
{required this.cellController, required this.completed, Key? key})
|
||||
const URLCellEditor({required this.cellController, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<URLCellEditor> createState() => _URLCellEditorState();
|
||||
|
||||
static void show(
|
||||
BuildContext context,
|
||||
GridURLCellController cellContext,
|
||||
VoidCallback completed,
|
||||
) {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
final editor = URLCellEditor(
|
||||
cellController: cellContext,
|
||||
completed: completed,
|
||||
);
|
||||
|
||||
//
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
constraints: BoxConstraints.loose(const Size(300, 160)),
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: Padding(padding: const EdgeInsets.all(6), child: editor),
|
||||
),
|
||||
),
|
||||
identifier: URLCellEditor.identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.bottomWithCenterAligned,
|
||||
delegate: editor,
|
||||
);
|
||||
}
|
||||
|
||||
static String identifier() {
|
||||
return (URLCellEditor).toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool asBarrier() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
void didRemove() {
|
||||
completed();
|
||||
}
|
||||
}
|
||||
|
||||
class _URLCellEditorState extends State<URLCellEditor> {
|
||||
@ -114,3 +71,25 @@ class _URLCellEditorState extends State<URLCellEditor> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class URLEditorPopover extends StatelessWidget {
|
||||
final GridURLCellController cellController;
|
||||
const URLEditorPopover({required this.cellController, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OverlayContainer(
|
||||
constraints: BoxConstraints.loose(const Size(300, 160)),
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: URLCellEditor(
|
||||
cellController: cellController,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/cell/url_cell_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy_popover/popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
@ -48,27 +49,37 @@ class GridURLCell extends GridCellWidget {
|
||||
@override
|
||||
GridCellState<GridURLCell> createState() => _GridURLCellState();
|
||||
|
||||
GridCellAccessory accessoryFromType(
|
||||
GridCellAccessoryBuilder accessoryFromType(
|
||||
GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) {
|
||||
switch (ty) {
|
||||
case GridURLCellAccessoryType.edit:
|
||||
final cellController =
|
||||
cellControllerBuilder.build() as GridURLCellController;
|
||||
return _EditURLAccessory(
|
||||
return GridCellAccessoryBuilder(
|
||||
builder: (Key key) => _EditURLAccessory(
|
||||
key: key,
|
||||
cellContext: cellController,
|
||||
anchorContext: buildContext.anchorContext);
|
||||
anchorContext: buildContext.anchorContext,
|
||||
),
|
||||
);
|
||||
|
||||
case GridURLCellAccessoryType.copyURL:
|
||||
final cellContext =
|
||||
cellControllerBuilder.build() as GridURLCellController;
|
||||
return _CopyURLAccessory(cellContext: cellContext);
|
||||
return GridCellAccessoryBuilder(
|
||||
builder: (Key key) => _CopyURLAccessory(
|
||||
key: key,
|
||||
cellContext: cellContext,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)
|
||||
List<GridCellAccessoryBuilder> Function(
|
||||
GridCellAccessoryBuildContext buildContext)
|
||||
get accessoryBuilder => (buildContext) {
|
||||
final List<GridCellAccessory> accessories = [];
|
||||
final List<GridCellAccessoryBuilder> accessories = [];
|
||||
if (cellStyle != null) {
|
||||
accessories.addAll(cellStyle!.accessoryTypes.map((ty) {
|
||||
return accessoryFromType(ty, buildContext);
|
||||
@ -86,6 +97,8 @@ class GridURLCell extends GridCellWidget {
|
||||
}
|
||||
|
||||
class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||
final _popoverController = PopoverController();
|
||||
GridURLCellController? _cellContext;
|
||||
late URLCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
@ -116,14 +129,28 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||
),
|
||||
);
|
||||
|
||||
return SizedBox.expand(
|
||||
return Popover(
|
||||
controller: _popoverController,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
offset: const Offset(0, 20),
|
||||
child: SizedBox.expand(
|
||||
child: GestureDetector(
|
||||
child: Align(alignment: Alignment.centerLeft, child: richText),
|
||||
onTap: () async {
|
||||
final url = context.read<URLCellBloc>().state.url;
|
||||
await _openUrlOrEdit(url);
|
||||
child: Align(alignment: Alignment.centerLeft, child: richText),
|
||||
onTap: () async {
|
||||
final url = context.read<URLCellBloc>().state.url;
|
||||
await _openUrlOrEdit(url);
|
||||
},
|
||||
),
|
||||
),
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
return URLEditorPopover(
|
||||
cellController: _cellContext!,
|
||||
);
|
||||
},
|
||||
));
|
||||
onClose: () {
|
||||
widget.onCellEditing.value = false;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -140,12 +167,10 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||
if (url.isNotEmpty && await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
final cellContext =
|
||||
_cellContext =
|
||||
widget.cellControllerBuilder.build() as GridURLCellController;
|
||||
widget.onCellEditing.value = true;
|
||||
URLCellEditor.show(context, cellContext, () {
|
||||
widget.onCellEditing.value = false;
|
||||
});
|
||||
_popoverController.show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +188,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||
}
|
||||
}
|
||||
|
||||
class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
class _EditURLAccessory extends StatefulWidget {
|
||||
final GridURLCellController cellContext;
|
||||
final BuildContext anchorContext;
|
||||
const _EditURLAccessory({
|
||||
@ -172,24 +197,55 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _EditURLAccessoryState();
|
||||
}
|
||||
|
||||
class _EditURLAccessoryState extends State<_EditURLAccessory>
|
||||
with GridCellAccessoryState {
|
||||
late PopoverController _popoverController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_popoverController = PopoverController();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return svgWidget("editor/edit", color: theme.iconColor);
|
||||
return Popover(
|
||||
controller: _popoverController,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
triggerActions: PopoverTriggerActionFlags.click,
|
||||
offset: const Offset(0, 20),
|
||||
child: svgWidget("editor/edit", color: theme.iconColor),
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
return URLEditorPopover(
|
||||
cellController: widget.cellContext.clone(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onTap() {
|
||||
URLCellEditor.show(anchorContext, cellContext, () {});
|
||||
_popoverController.show();
|
||||
}
|
||||
}
|
||||
|
||||
class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
class _CopyURLAccessory extends StatefulWidget {
|
||||
final GridURLCellController cellContext;
|
||||
const _CopyURLAccessory({required this.cellContext, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CopyURLAccessoryState();
|
||||
}
|
||||
|
||||
class _CopyURLAccessoryState extends State<_CopyURLAccessory>
|
||||
with GridCellAccessoryState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return svgWidget("editor/copy", color: theme.iconColor);
|
||||
@ -198,7 +254,7 @@ class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||
@override
|
||||
void onTap() {
|
||||
final content =
|
||||
cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
|
||||
widget.cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
|
||||
Clipboard.setData(ClipboardData(text: content));
|
||||
showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
|
||||
}
|
||||
|
@ -194,12 +194,17 @@ class RowContent extends StatelessWidget {
|
||||
Provider.of<RegionStateNotifier>(context, listen: false),
|
||||
accessoryBuilder: (buildContext) {
|
||||
final builder = child.accessoryBuilder;
|
||||
List<GridCellAccessory> accessories = [];
|
||||
List<GridCellAccessoryBuilder> accessories = [];
|
||||
if (cellId.field.isPrimary) {
|
||||
accessories.add(PrimaryCellAccessory(
|
||||
onTapCallback: onExpand,
|
||||
isCellEditing: buildContext.isCellEditing,
|
||||
));
|
||||
accessories.add(
|
||||
GridCellAccessoryBuilder(
|
||||
builder: (key) => PrimaryCellAccessory(
|
||||
key: key,
|
||||
onTapCallback: onExpand,
|
||||
isCellEditing: buildContext.isCellEditing,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (builder != null) {
|
||||
|
Loading…
Reference in New Issue
Block a user