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:
Daniyar Nurmukhamet 2023-05-30 09:04:10 +06:00 committed by GitHub
parent 32650b7941
commit f022f9aa06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 48 deletions

View 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

View File

@ -19,6 +19,8 @@ import 'url_cell_bloc.dart';
class GridURLCellStyle extends GridCellStyle {
String? placeholder;
TextStyle? textStyle;
bool? autofocus;
List<GridURLCellAccessoryType> accessoryTypes;
@ -29,8 +31,8 @@ class GridURLCellStyle extends GridCellStyle {
}
enum GridURLCellAccessoryType {
edit,
copyURL,
visitURL,
}
class GridURLCell extends GridCellWidget {
@ -56,15 +58,14 @@ class GridURLCell extends GridCellWidget {
GridCellAccessoryBuildContext buildContext,
) {
switch (ty) {
case GridURLCellAccessoryType.edit:
case GridURLCellAccessoryType.visitURL:
final cellContext = cellControllerBuilder.build() as URLCellController;
return GridCellAccessoryBuilder(
builder: (Key key) => _EditURLAccessory(
builder: (Key key) => _VisitURLAccessory(
key: key,
anchorContext: buildContext.anchorContext,
cellControllerBuilder: cellControllerBuilder,
cellContext: cellContext,
),
);
case GridURLCellAccessoryType.copyURL:
final cellContext = cellControllerBuilder.build() as URLCellController;
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) {
accessories.add(
accessoryFromType(
GridURLCellAccessoryType.edit,
GridURLCellAccessoryType.visitURL,
buildContext,
),
);
@ -106,6 +107,8 @@ class GridURLCell extends GridCellWidget {
class _GridURLCellState extends GridCellState<GridURLCell> {
final _popoverController = PopoverController();
late URLCellBloc _cellBloc;
late TextEditingController _controller;
late FocusNode _focusNode;
@override
void initState() {
@ -113,6 +116,8 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
widget.cellControllerBuilder.build() as URLCellController;
_cellBloc = URLCellBloc(cellController: cellController);
_cellBloc.add(const URLCellEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);
_focusNode = FocusNode();
super.initState();
}
@ -122,60 +127,61 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
value: _cellBloc,
child: BlocBuilder<URLCellBloc, URLCellState>(
builder: (context, state) {
final richText = Padding(
padding: GridSize.cellContentInsets,
child: FlowyText.medium(
state.content,
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
final urlEditor = Padding(
padding: EdgeInsets.only(
left: GridSize.cellContentInsets.left,
right: GridSize.cellContentInsets.right,
),
);
return AppFlowyPopover(
margin: EdgeInsets.zero,
controller: _popoverController,
constraints: BoxConstraints.loose(const Size(300, 160)),
direction: PopoverDirection.bottomWithLeftAligned,
triggerActions: PopoverTriggerFlags.none,
offset: const Offset(0, 8),
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: TextField(
controller: _controller,
focusNode: _focusNode,
maxLines: 1,
style: (widget.cellStyle?.textStyle ??
Theme.of(context).textTheme.bodyMedium)
?.copyWith(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
autofocus: false,
onEditingComplete: focusChanged,
onSubmitted: (value) => focusChanged(isUrlSubmitted: true),
decoration: InputDecoration(
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
Future<void> dispose() async {
_cellBloc.close();
super.dispose();
}
Future<void> _openUrlOrEdit(String url) async {
final uri = Uri.parse(url);
if (url.isNotEmpty && await canLaunchUrl(uri)) {
await launchUrl(uri);
}
}
@override
void requestBeginFocus() {
widget.onCellEditing.value = true;
@ -269,3 +275,36 @@ class _CopyURLAccessoryState extends State<_CopyURLAccessory>
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));
}
}
}

View File

@ -361,8 +361,8 @@ GridCellStyle? _customCellStyle(FieldType fieldType) {
return GridURLCellStyle(
placeholder: LocaleKeys.grid_row_textPlaceholder.tr(),
accessoryTypes: [
GridURLCellAccessoryType.edit,
GridURLCellAccessoryType.copyURL,
GridURLCellAccessoryType.visitURL,
],
);
}