|
@@ -0,0 +1,173 @@
|
|
|
+import 'package:appflowy/generated/locale_keys.g.dart';
|
|
|
+import 'package:appflowy_editor/appflowy_editor.dart';
|
|
|
+import 'package:appflowy_popover/appflowy_popover.dart';
|
|
|
+import 'package:easy_localization/easy_localization.dart';
|
|
|
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|
|
+import 'package:flowy_infra_ui/style_widget/text_input.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter_math_fork/flutter_math.dart';
|
|
|
+import 'package:provider/provider.dart';
|
|
|
+
|
|
|
+class InlineMathEquationKeys {
|
|
|
+ const InlineMathEquationKeys._();
|
|
|
+
|
|
|
+ static const formula = 'formula';
|
|
|
+}
|
|
|
+
|
|
|
+class InlineMathEquation extends StatefulWidget {
|
|
|
+ const InlineMathEquation({
|
|
|
+ super.key,
|
|
|
+ required this.formula,
|
|
|
+ required this.node,
|
|
|
+ required this.index,
|
|
|
+ this.textStyle,
|
|
|
+ });
|
|
|
+
|
|
|
+ final Node node;
|
|
|
+ final int index;
|
|
|
+ final String formula;
|
|
|
+ final TextStyle? textStyle;
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<InlineMathEquation> createState() => _InlineMathEquationState();
|
|
|
+}
|
|
|
+
|
|
|
+class _InlineMathEquationState extends State<InlineMathEquation> {
|
|
|
+ final popoverController = PopoverController();
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ final theme = Theme.of(context);
|
|
|
+ return _IgnoreParentPointer(
|
|
|
+ child: AppFlowyPopover(
|
|
|
+ controller: popoverController,
|
|
|
+ direction: PopoverDirection.bottomWithLeftAligned,
|
|
|
+ popupBuilder: (_) {
|
|
|
+ return MathInputTextField(
|
|
|
+ initialText: widget.formula,
|
|
|
+ onSubmit: (value) async {
|
|
|
+ popoverController.close();
|
|
|
+ if (value == widget.formula) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ final editorState = context.read<EditorState>();
|
|
|
+ final transaction = editorState.transaction
|
|
|
+ ..formatText(widget.node, widget.index, 1, {
|
|
|
+ InlineMathEquationKeys.formula: value,
|
|
|
+ });
|
|
|
+ await editorState.apply(transaction);
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ offset: const Offset(0, 10),
|
|
|
+ child: Padding(
|
|
|
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
|
|
|
+ child: MouseRegion(
|
|
|
+ cursor: SystemMouseCursors.click,
|
|
|
+ child: Row(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ children: [
|
|
|
+ const HSpace(2),
|
|
|
+ Math.tex(
|
|
|
+ widget.formula,
|
|
|
+ options: MathOptions(
|
|
|
+ style: MathStyle.text,
|
|
|
+ mathFontOptions: const FontOptions(
|
|
|
+ fontShape: FontStyle.italic,
|
|
|
+ ),
|
|
|
+ fontSize: 14.0,
|
|
|
+ color: widget.textStyle?.color ??
|
|
|
+ theme.colorScheme.onBackground,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const HSpace(2),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class MathInputTextField extends StatefulWidget {
|
|
|
+ const MathInputTextField({
|
|
|
+ super.key,
|
|
|
+ required this.initialText,
|
|
|
+ required this.onSubmit,
|
|
|
+ });
|
|
|
+
|
|
|
+ final String initialText;
|
|
|
+ final void Function(String value) onSubmit;
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<MathInputTextField> createState() => _MathInputTextFieldState();
|
|
|
+}
|
|
|
+
|
|
|
+class _MathInputTextFieldState extends State<MathInputTextField> {
|
|
|
+ late final TextEditingController textEditingController;
|
|
|
+
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ super.initState();
|
|
|
+
|
|
|
+ textEditingController = TextEditingController(
|
|
|
+ text: widget.initialText,
|
|
|
+ );
|
|
|
+ textEditingController.selection = TextSelection(
|
|
|
+ baseOffset: 0,
|
|
|
+ extentOffset: widget.initialText.length,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return SizedBox(
|
|
|
+ width: 240,
|
|
|
+ child: Row(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: FlowyFormTextInput(
|
|
|
+ autoFocus: true,
|
|
|
+ textAlign: TextAlign.left,
|
|
|
+ controller: textEditingController,
|
|
|
+ contentPadding: const EdgeInsets.symmetric(
|
|
|
+ vertical: 8.0,
|
|
|
+ horizontal: 4.0,
|
|
|
+ ),
|
|
|
+ onEditingComplete: () =>
|
|
|
+ widget.onSubmit(textEditingController.text),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const HSpace(4.0),
|
|
|
+ FlowyButton(
|
|
|
+ text: FlowyText(LocaleKeys.button_Done.tr()),
|
|
|
+ useIntrinsicWidth: true,
|
|
|
+ onTap: () => widget.onSubmit(textEditingController.text),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _IgnoreParentPointer extends StatelessWidget {
|
|
|
+ const _IgnoreParentPointer({
|
|
|
+ required this.child,
|
|
|
+ });
|
|
|
+
|
|
|
+ final Widget child;
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return GestureDetector(
|
|
|
+ behavior: HitTestBehavior.opaque,
|
|
|
+ onTap: () {},
|
|
|
+ onTapDown: (_) {},
|
|
|
+ onDoubleTap: () {},
|
|
|
+ onLongPress: () {},
|
|
|
+ child: child,
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|