mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: implement command + K to trigger link menu
This commit is contained in:
parent
1f90f30274
commit
886c1f00e5
@ -21,12 +21,21 @@ class LinkMenu extends StatefulWidget {
|
||||
|
||||
class _LinkMenuState extends State<LinkMenu> {
|
||||
final _textEditingController = TextEditingController();
|
||||
final _focusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_textEditingController.text = widget.linkText ?? '';
|
||||
_focusNode.requestFocus();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -88,7 +97,7 @@ class _LinkMenuState extends State<LinkMenu> {
|
||||
|
||||
Widget _buildInput() {
|
||||
return TextField(
|
||||
autofocus: true,
|
||||
focusNode: _focusNode,
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
textAlign: TextAlign.left,
|
||||
controller: _textEditingController,
|
||||
@ -112,11 +121,7 @@ class _LinkMenuState extends State<LinkMenu> {
|
||||
required VoidCallback onPressed,
|
||||
}) {
|
||||
return TextButton.icon(
|
||||
icon: FlowySvg(
|
||||
name: iconName,
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
),
|
||||
icon: FlowySvg(name: iconName),
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(40),
|
||||
padding: EdgeInsets.zero,
|
||||
|
@ -13,12 +13,14 @@ typedef ToolbarShowValidator = bool Function(EditorState editorState);
|
||||
|
||||
class ToolbarItem {
|
||||
ToolbarItem({
|
||||
required this.id,
|
||||
required this.icon,
|
||||
this.tooltipsMessage = '',
|
||||
required this.validator,
|
||||
required this.handler,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final Widget icon;
|
||||
final String tooltipsMessage;
|
||||
final ToolbarShowValidator validator;
|
||||
@ -26,6 +28,7 @@ class ToolbarItem {
|
||||
|
||||
factory ToolbarItem.divider() {
|
||||
return ToolbarItem(
|
||||
id: 'divider',
|
||||
icon: const FlowySvg(name: 'toolbar/divider'),
|
||||
validator: (editorState) => true,
|
||||
handler: (editorState, context) {},
|
||||
@ -35,18 +38,21 @@ class ToolbarItem {
|
||||
|
||||
List<ToolbarItem> defaultToolbarItems = [
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.h1',
|
||||
tooltipsMessage: 'Heading 1',
|
||||
icon: const FlowySvg(name: 'toolbar/h1'),
|
||||
validator: _onlyShowInSingleTextSelection,
|
||||
handler: (editorState, context) => formatHeading(editorState, StyleKey.h1),
|
||||
),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.h2',
|
||||
tooltipsMessage: 'Heading 2',
|
||||
icon: const FlowySvg(name: 'toolbar/h2'),
|
||||
validator: _onlyShowInSingleTextSelection,
|
||||
handler: (editorState, context) => formatHeading(editorState, StyleKey.h2),
|
||||
),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.h3',
|
||||
tooltipsMessage: 'Heading 3',
|
||||
icon: const FlowySvg(name: 'toolbar/h3'),
|
||||
validator: _onlyShowInSingleTextSelection,
|
||||
@ -54,24 +60,28 @@ List<ToolbarItem> defaultToolbarItems = [
|
||||
),
|
||||
ToolbarItem.divider(),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.bold',
|
||||
tooltipsMessage: 'Bold',
|
||||
icon: const FlowySvg(name: 'toolbar/bold'),
|
||||
validator: _showInTextSelection,
|
||||
handler: (editorState, context) => formatBold(editorState),
|
||||
),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.italic',
|
||||
tooltipsMessage: 'Italic',
|
||||
icon: const FlowySvg(name: 'toolbar/italic'),
|
||||
validator: _showInTextSelection,
|
||||
handler: (editorState, context) => formatItalic(editorState),
|
||||
),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.underline',
|
||||
tooltipsMessage: 'Underline',
|
||||
icon: const FlowySvg(name: 'toolbar/underline'),
|
||||
validator: _showInTextSelection,
|
||||
handler: (editorState, context) => formatUnderline(editorState),
|
||||
),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.strikethrough',
|
||||
tooltipsMessage: 'Strikethrough',
|
||||
icon: const FlowySvg(name: 'toolbar/strikethrough'),
|
||||
validator: _showInTextSelection,
|
||||
@ -79,12 +89,14 @@ List<ToolbarItem> defaultToolbarItems = [
|
||||
),
|
||||
ToolbarItem.divider(),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.quote',
|
||||
tooltipsMessage: 'Quote',
|
||||
icon: const FlowySvg(name: 'toolbar/quote'),
|
||||
validator: _onlyShowInSingleTextSelection,
|
||||
handler: (editorState, context) => formatQuote(editorState),
|
||||
),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.bulleted_list',
|
||||
tooltipsMessage: 'Bulleted list',
|
||||
icon: const FlowySvg(name: 'toolbar/bulleted_list'),
|
||||
validator: _onlyShowInSingleTextSelection,
|
||||
@ -92,12 +104,14 @@ List<ToolbarItem> defaultToolbarItems = [
|
||||
),
|
||||
ToolbarItem.divider(),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.link',
|
||||
tooltipsMessage: 'Link',
|
||||
icon: const FlowySvg(name: 'toolbar/link'),
|
||||
validator: _onlyShowInSingleTextSelection,
|
||||
handler: (editorState, context) => _showLinkMenu(editorState, context),
|
||||
),
|
||||
ToolbarItem(
|
||||
id: 'appflowy.toolbar.highlight',
|
||||
tooltipsMessage: 'Highlight',
|
||||
icon: const FlowySvg(name: 'toolbar/highlight'),
|
||||
validator: _showInTextSelection,
|
||||
@ -152,9 +166,7 @@ void _showLinkMenu(EditorState editorState, BuildContext context) {
|
||||
linkText: linkText,
|
||||
onSubmitted: (text) {
|
||||
TransactionBuilder(editorState)
|
||||
..formatText(node, index, length, {
|
||||
StyleKey.href: text,
|
||||
})
|
||||
..formatText(node, index, length, {StyleKey.href: text})
|
||||
..commit();
|
||||
_dismissLinkMenu();
|
||||
},
|
||||
@ -164,9 +176,7 @@ void _showLinkMenu(EditorState editorState, BuildContext context) {
|
||||
},
|
||||
onRemoveLink: () {
|
||||
TransactionBuilder(editorState)
|
||||
..formatText(node, index, length, {
|
||||
StyleKey.href: null,
|
||||
})
|
||||
..formatText(node, index, length, {StyleKey.href: null})
|
||||
..commit();
|
||||
_dismissLinkMenu();
|
||||
},
|
||||
@ -177,6 +187,7 @@ void _showLinkMenu(EditorState editorState, BuildContext context) {
|
||||
Overlay.of(context)?.insert(_linkMenuOverlay!);
|
||||
|
||||
editorState.service.scrollService?.disable();
|
||||
editorState.service.keyboardService?.disable();
|
||||
editorState.service.selectionService.currentSelection
|
||||
.addListener(_dismissLinkMenu);
|
||||
}
|
||||
@ -186,6 +197,7 @@ void _dismissLinkMenu() {
|
||||
_linkMenuOverlay = null;
|
||||
|
||||
_editorState?.service.scrollService?.enable();
|
||||
_editorState?.service.keyboardService?.enable();
|
||||
_editorState?.service.selectionService.currentSelection
|
||||
.removeListener(_dismissLinkMenu);
|
||||
_editorState = null;
|
||||
|
@ -36,6 +36,12 @@ AppFlowyKeyEventHandler updateTextStyleByCommandXHandler =
|
||||
event.isShiftPressed) {
|
||||
formatHighlight(editorState);
|
||||
return KeyEventResult.handled;
|
||||
} else if (event.logicalKey == LogicalKeyboardKey.keyK) {
|
||||
if (editorState.service.toolbarService
|
||||
?.triggerHandler('appflowy.toolbar.link') ==
|
||||
true) {
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
}
|
||||
|
||||
return KeyEventResult.ignored;
|
||||
|
@ -11,6 +11,9 @@ abstract class FlowyToolbarService {
|
||||
|
||||
/// Hide the toolbar widget.
|
||||
void hide();
|
||||
|
||||
/// Trigger the specified handler.
|
||||
bool triggerHandler(String id);
|
||||
}
|
||||
|
||||
class FlowyToolbar extends StatefulWidget {
|
||||
@ -55,6 +58,17 @@ class _FlowyToolbarState extends State<FlowyToolbar>
|
||||
_toolbarOverlay = null;
|
||||
}
|
||||
|
||||
@override
|
||||
bool triggerHandler(String id) {
|
||||
final items = defaultToolbarItems.where((item) => item.id == id);
|
||||
if (items.length != 1) {
|
||||
assert(items.length == 1, 'The toolbar item\'s id must be unique');
|
||||
return false;
|
||||
}
|
||||
items.first.handler(widget.editorState, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
Loading…
Reference in New Issue
Block a user