feat: move math equation plugin to appflowy editor plugins directory

This commit is contained in:
Lucas.Xu 2022-12-01 14:44:06 +08:00
parent 157f929ff9
commit 2b27fe85aa
4 changed files with 223 additions and 0 deletions

View File

@ -37,13 +37,20 @@ class SimpleEditor extends StatelessWidget {
themeData: themeData,
autoFocus: editorState.document.isEmpty,
customBuilders: {
// Divider
kDividerType: DividerWidgetBuilder(),
// Math Equation
kMathEquationType: MathEquationNodeWidgetBuidler(),
},
shortcutEvents: [
// Divider
insertDividerEvent,
],
selectionMenuItems: [
// Divider
dividerMenuItem,
// Math Equation
mathEquationMenuItem,
],
);
} else {

View File

@ -1,4 +1,8 @@
library appflowy_editor_plugins;
// Divider
export 'src/divider/divider_node_widget.dart';
export 'src/divider/divider_shortcut_event.dart';
// Math Equation
export 'src/math_ equation/math_equation_node_widget.dart';

View File

@ -0,0 +1,211 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_math_fork/flutter_math.dart';
const String kMathEquationType = 'math_equation';
const String kMathEquationAttr = 'math_equation';
// TODO: l10n
SelectionMenuItem mathEquationMenuItem = SelectionMenuItem(
name: () => 'Math Equation',
icon: (editorState, onSelected) => Icon(
Icons.text_fields_rounded,
color: onSelected
? editorState.editorStyle.selectionMenuItemSelectedIconColor
: editorState.editorStyle.selectionMenuItemIconColor,
size: 18.0,
),
keywords: ['tex, latex, katex', 'math equation'],
handler: (editorState, _, __) {
final selection =
editorState.service.selectionService.currentSelection.value;
final textNodes = editorState.service.selectionService.currentSelectedNodes
.whereType<TextNode>();
if (selection == null || textNodes.isEmpty) {
return;
}
final textNode = textNodes.first;
final Path mathEquationNodePath;
if (textNode.toPlainText().isEmpty) {
mathEquationNodePath = selection.end.path;
} else {
mathEquationNodePath = selection.end.path.next;
}
// insert the math equation node
final transaction = editorState.transaction
..insertNode(
mathEquationNodePath,
Node(type: kMathEquationType, attributes: {kMathEquationAttr: ''}),
)
..afterSelection = selection;
editorState.apply(transaction);
// tricy to show the editing dialog.
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final mathEquationState = editorState.document
.nodeAtPath(mathEquationNodePath)
?.key
?.currentState;
if (mathEquationState != null &&
mathEquationState is _MathEquationNodeWidgetState) {
mathEquationState.showEditingDialog();
}
});
},
);
class MathEquationNodeWidgetBuidler extends NodeWidgetBuilder<Node> {
@override
Widget build(NodeWidgetContext<Node> context) {
return _MathEquationNodeWidget(
key: context.node.key,
node: context.node,
editorState: context.editorState,
);
}
@override
NodeValidator<Node> get nodeValidator =>
(node) => node.attributes[kMathEquationAttr] is String;
}
class _MathEquationNodeWidget extends StatefulWidget {
const _MathEquationNodeWidget({
Key? key,
required this.node,
required this.editorState,
}) : super(key: key);
final Node node;
final EditorState editorState;
@override
State<_MathEquationNodeWidget> createState() =>
_MathEquationNodeWidgetState();
}
class _MathEquationNodeWidgetState extends State<_MathEquationNodeWidget> {
String get _mathEquation =>
widget.node.attributes[kMathEquationAttr] as String;
bool _isHover = false;
@override
Widget build(BuildContext context) {
return InkWell(
onHover: (value) {
setState(() {
_isHover = value;
});
},
onTap: () {
showEditingDialog();
},
child: Stack(
children: [
_buildMathEquation(context),
if (_isHover) _buildDeleteButton(context),
],
),
);
}
Widget _buildMathEquation(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
padding: const EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
color: _isHover ? Colors.grey[200] : Colors.transparent,
),
child: Center(
child: Math.tex(
_mathEquation,
textStyle: const TextStyle(fontSize: 20),
mathStyle: MathStyle.display,
),
),
);
}
Widget _buildDeleteButton(BuildContext context) {
return Positioned(
top: -5,
right: -5,
child: IconButton(
icon: Icon(
Icons.delete_forever_outlined,
color: widget.editorState.editorStyle.selectionMenuItemIconColor,
size: 16,
),
onPressed: () {
final transaction = widget.editorState.transaction
..deleteNode(widget.node);
widget.editorState.apply(transaction);
},
),
);
}
void showEditingDialog() {
showDialog(
context: context,
builder: (context) {
final controller = TextEditingController(text: _mathEquation);
return AlertDialog(
title: const Text('Edit Math Equation'),
content: RawKeyboardListener(
focusNode: FocusNode(),
onKey: (key) {
if (key is! RawKeyDownEvent) return;
if (key.logicalKey == LogicalKeyboardKey.enter &&
!key.isShiftPressed) {
_updateMathEquation(controller.text);
} else if (key.logicalKey == LogicalKeyboardKey.escape) {
_dismiss();
}
},
child: TextField(
autofocus: true,
controller: controller,
maxLines: null,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'E = MC^2',
),
),
),
actions: [
TextButton(
onPressed: () => _dismiss(),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => _updateMathEquation(controller.text),
child: const Text('Done'),
),
],
);
},
);
}
void _updateMathEquation(String mathEquation) {
_dismiss();
if (mathEquation == _mathEquation) {
return;
}
final transaction = widget.editorState.transaction;
transaction.updateNode(
widget.node,
{
kMathEquationAttr: mathEquation,
},
);
widget.editorState.apply(transaction);
}
void _dismiss() {
Navigator.of(context).pop();
}
}

View File

@ -14,6 +14,7 @@ dependencies:
sdk: flutter
appflowy_editor:
path: ../appflowy_editor
flutter_math_fork: ^0.6.3+1
dev_dependencies:
flutter_test: