mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feature: changed url field ux to be directly editable (#2523)
* feature: changed url field ux to be directly editable * feature: added link svg icon
This commit is contained in:
parent
32650b7941
commit
f022f9aa06
4
frontend/appflowy_flutter/assets/images/editor/link.svg
Normal file
4
frontend/appflowy_flutter/assets/images/editor/link.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M7 8.91521C7.21574 9.22582 7.49099 9.48283 7.80707 9.66881C8.12315 9.85479 8.47268 9.96538 8.83194 9.99309C9.1912 10.0208 9.5518 9.96497 9.88926 9.8294C10.2267 9.69383 10.5332 9.48169 10.7878 9.20736L12.2949 7.58431C12.7525 7.07413 13.0056 6.39083 12.9999 5.68156C12.9942 4.9723 12.73 4.29384 12.2643 3.7923C11.7986 3.29075 11.1686 3.00627 10.51 3.0001C9.85142 2.99394 9.21693 3.26659 8.7432 3.75935L7.87913 4.68448" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9 7.08479C8.78426 6.77418 8.50901 6.51717 8.19293 6.33119C7.87685 6.14521 7.52732 6.03462 7.16806 6.00691C6.8088 5.9792 6.4482 6.03503 6.11074 6.1706C5.77327 6.30617 5.46683 6.51831 5.21218 6.79264L3.7051 8.41569C3.24755 8.92587 2.99437 9.60918 3.00009 10.3184C3.00582 11.0277 3.26998 11.7062 3.73569 12.2077C4.2014 12.7092 4.8314 12.9937 5.48999 12.9999C6.14858 13.0061 6.78307 12.7334 7.2568 12.2407L8.11584 11.3155" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -19,6 +19,8 @@ import 'url_cell_bloc.dart';
|
|||||||
|
|
||||||
class GridURLCellStyle extends GridCellStyle {
|
class GridURLCellStyle extends GridCellStyle {
|
||||||
String? placeholder;
|
String? placeholder;
|
||||||
|
TextStyle? textStyle;
|
||||||
|
bool? autofocus;
|
||||||
|
|
||||||
List<GridURLCellAccessoryType> accessoryTypes;
|
List<GridURLCellAccessoryType> accessoryTypes;
|
||||||
|
|
||||||
@ -29,8 +31,8 @@ class GridURLCellStyle extends GridCellStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GridURLCellAccessoryType {
|
enum GridURLCellAccessoryType {
|
||||||
edit,
|
|
||||||
copyURL,
|
copyURL,
|
||||||
|
visitURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridURLCell extends GridCellWidget {
|
class GridURLCell extends GridCellWidget {
|
||||||
@ -56,15 +58,14 @@ class GridURLCell extends GridCellWidget {
|
|||||||
GridCellAccessoryBuildContext buildContext,
|
GridCellAccessoryBuildContext buildContext,
|
||||||
) {
|
) {
|
||||||
switch (ty) {
|
switch (ty) {
|
||||||
case GridURLCellAccessoryType.edit:
|
case GridURLCellAccessoryType.visitURL:
|
||||||
|
final cellContext = cellControllerBuilder.build() as URLCellController;
|
||||||
return GridCellAccessoryBuilder(
|
return GridCellAccessoryBuilder(
|
||||||
builder: (Key key) => _EditURLAccessory(
|
builder: (Key key) => _VisitURLAccessory(
|
||||||
key: key,
|
key: key,
|
||||||
anchorContext: buildContext.anchorContext,
|
cellContext: cellContext,
|
||||||
cellControllerBuilder: cellControllerBuilder,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
case GridURLCellAccessoryType.copyURL:
|
case GridURLCellAccessoryType.copyURL:
|
||||||
final cellContext = cellControllerBuilder.build() as URLCellController;
|
final cellContext = cellControllerBuilder.build() as URLCellController;
|
||||||
return GridCellAccessoryBuilder(
|
return GridCellAccessoryBuilder(
|
||||||
@ -89,11 +90,11 @@ class GridURLCell extends GridCellWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the accessories is empty then the default accessory will be GridURLCellAccessoryType.edit
|
// If the accessories is empty then the default accessory will be GridURLCellAccessoryType.visitURL
|
||||||
if (accessories.isEmpty) {
|
if (accessories.isEmpty) {
|
||||||
accessories.add(
|
accessories.add(
|
||||||
accessoryFromType(
|
accessoryFromType(
|
||||||
GridURLCellAccessoryType.edit,
|
GridURLCellAccessoryType.visitURL,
|
||||||
buildContext,
|
buildContext,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -106,6 +107,8 @@ class GridURLCell extends GridCellWidget {
|
|||||||
class _GridURLCellState extends GridCellState<GridURLCell> {
|
class _GridURLCellState extends GridCellState<GridURLCell> {
|
||||||
final _popoverController = PopoverController();
|
final _popoverController = PopoverController();
|
||||||
late URLCellBloc _cellBloc;
|
late URLCellBloc _cellBloc;
|
||||||
|
late TextEditingController _controller;
|
||||||
|
late FocusNode _focusNode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -113,6 +116,8 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
|||||||
widget.cellControllerBuilder.build() as URLCellController;
|
widget.cellControllerBuilder.build() as URLCellController;
|
||||||
_cellBloc = URLCellBloc(cellController: cellController);
|
_cellBloc = URLCellBloc(cellController: cellController);
|
||||||
_cellBloc.add(const URLCellEvent.initial());
|
_cellBloc.add(const URLCellEvent.initial());
|
||||||
|
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||||
|
_focusNode = FocusNode();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,60 +127,61 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
|||||||
value: _cellBloc,
|
value: _cellBloc,
|
||||||
child: BlocBuilder<URLCellBloc, URLCellState>(
|
child: BlocBuilder<URLCellBloc, URLCellState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final richText = Padding(
|
final urlEditor = Padding(
|
||||||
padding: GridSize.cellContentInsets,
|
padding: EdgeInsets.only(
|
||||||
child: FlowyText.medium(
|
left: GridSize.cellContentInsets.left,
|
||||||
state.content,
|
right: GridSize.cellContentInsets.right,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
),
|
||||||
);
|
child: TextField(
|
||||||
|
controller: _controller,
|
||||||
return AppFlowyPopover(
|
focusNode: _focusNode,
|
||||||
margin: EdgeInsets.zero,
|
maxLines: 1,
|
||||||
controller: _popoverController,
|
style: (widget.cellStyle?.textStyle ??
|
||||||
constraints: BoxConstraints.loose(const Size(300, 160)),
|
Theme.of(context).textTheme.bodyMedium)
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
?.copyWith(
|
||||||
triggerActions: PopoverTriggerFlags.none,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
offset: const Offset(0, 8),
|
decoration: TextDecoration.underline,
|
||||||
child: SizedBox.expand(
|
),
|
||||||
child: GestureDetector(
|
autofocus: false,
|
||||||
child: Align(alignment: Alignment.centerLeft, child: richText),
|
onEditingComplete: focusChanged,
|
||||||
onTap: () async {
|
onSubmitted: (value) => focusChanged(isUrlSubmitted: true),
|
||||||
final url = context.read<URLCellBloc>().state.url;
|
decoration: InputDecoration(
|
||||||
await _openUrlOrEdit(url);
|
contentPadding: EdgeInsets.only(
|
||||||
},
|
top: GridSize.cellContentInsets.top,
|
||||||
|
bottom: GridSize.cellContentInsets.bottom,
|
||||||
|
),
|
||||||
|
border: InputBorder.none,
|
||||||
|
hintText: widget.cellStyle?.placeholder,
|
||||||
|
isDense: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
popupBuilder: (BuildContext popoverContext) {
|
|
||||||
return URLEditorPopover(
|
|
||||||
cellController:
|
|
||||||
widget.cellControllerBuilder.build() as URLCellController,
|
|
||||||
onExit: () => _popoverController.close(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onClose: () {
|
|
||||||
widget.onCellEditing.value = false;
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
return urlEditor;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void focusChanged({
|
||||||
|
bool isUrlSubmitted = false,
|
||||||
|
}) {
|
||||||
|
if (mounted) {
|
||||||
|
if (_cellBloc.isClosed == false &&
|
||||||
|
_controller.text != _cellBloc.state.content) {
|
||||||
|
_cellBloc.add(URLCellEvent.updateURL(_controller.text));
|
||||||
|
}
|
||||||
|
if (isUrlSubmitted) {
|
||||||
|
_focusNode.unfocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
_cellBloc.close();
|
_cellBloc.close();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openUrlOrEdit(String url) async {
|
|
||||||
final uri = Uri.parse(url);
|
|
||||||
if (url.isNotEmpty && await canLaunchUrl(uri)) {
|
|
||||||
await launchUrl(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void requestBeginFocus() {
|
void requestBeginFocus() {
|
||||||
widget.onCellEditing.value = true;
|
widget.onCellEditing.value = true;
|
||||||
@ -269,3 +275,36 @@ class _CopyURLAccessoryState extends State<_CopyURLAccessory>
|
|||||||
showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
|
showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _VisitURLAccessory extends StatefulWidget {
|
||||||
|
final URLCellController cellContext;
|
||||||
|
const _VisitURLAccessory({required this.cellContext, Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _VisitURLAccessoryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VisitURLAccessoryState extends State<_VisitURLAccessory>
|
||||||
|
with GridCellAccessoryState {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return svgWidget(
|
||||||
|
"editor/link",
|
||||||
|
color: AFThemeExtension.of(context).textColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onTap() {
|
||||||
|
var content =
|
||||||
|
widget.cellContext.getCellData(loadIfNotExist: false)?.content ?? "";
|
||||||
|
if (!content.contains('http://')) {
|
||||||
|
content = 'http://$content';
|
||||||
|
}
|
||||||
|
final uri = Uri.parse(content);
|
||||||
|
if (content.isNotEmpty) {
|
||||||
|
canLaunchUrl(uri).then((value) => launchUrl(uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -361,8 +361,8 @@ GridCellStyle? _customCellStyle(FieldType fieldType) {
|
|||||||
return GridURLCellStyle(
|
return GridURLCellStyle(
|
||||||
placeholder: LocaleKeys.grid_row_textPlaceholder.tr(),
|
placeholder: LocaleKeys.grid_row_textPlaceholder.tr(),
|
||||||
accessoryTypes: [
|
accessoryTypes: [
|
||||||
GridURLCellAccessoryType.edit,
|
|
||||||
GridURLCellAccessoryType.copyURL,
|
GridURLCellAccessoryType.copyURL,
|
||||||
|
GridURLCellAccessoryType.visitURL,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user