|  | @@ -7,12 +7,12 @@ import 'package:flowy_infra/theme.dart';
 | 
	
		
			
				|  |  |  import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 | 
	
		
			
				|  |  |  import 'package:flowy_infra_ui/style_widget/button.dart';
 | 
	
		
			
				|  |  |  import 'package:flowy_infra_ui/style_widget/text.dart';
 | 
	
		
			
				|  |  | +import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
 | 
	
		
			
				|  |  |  import 'package:flowy_infra_ui/widget/spacing.dart';
 | 
	
		
			
				|  |  |  import 'package:flowy_sdk/log.dart';
 | 
	
		
			
				|  |  |  import 'package:flutter/material.dart';
 | 
	
		
			
				|  |  |  import 'package:flutter_bloc/flutter_bloc.dart';
 | 
	
		
			
				|  |  |  import 'package:app_flowy/generated/locale_keys.g.dart';
 | 
	
		
			
				|  |  | -import 'field_name_input.dart';
 | 
	
		
			
				|  |  |  import 'field_type_option_editor.dart';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class FieldEditor extends StatefulWidget {
 | 
	
	
		
			
				|  | @@ -44,6 +44,12 @@ class _FieldEditorState extends State<FieldEditor> {
 | 
	
		
			
				|  |  |      super.initState();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  @override
 | 
	
		
			
				|  |  | +  void dispose() {
 | 
	
		
			
				|  |  | +    popoverMutex.dispose();
 | 
	
		
			
				|  |  | +    super.dispose();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    @override
 | 
	
		
			
				|  |  |    Widget build(BuildContext context) {
 | 
	
		
			
				|  |  |      return BlocProvider(
 | 
	
	
		
			
				|  | @@ -58,21 +64,14 @@ class _FieldEditorState extends State<FieldEditor> {
 | 
	
		
			
				|  |  |            return ListView(
 | 
	
		
			
				|  |  |              shrinkWrap: true,
 | 
	
		
			
				|  |  |              children: [
 | 
	
		
			
				|  |  | -              FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(),
 | 
	
		
			
				|  |  | -                  fontSize: 12),
 | 
	
		
			
				|  |  | -              const VSpace(10),
 | 
	
		
			
				|  |  | -              const _FieldNameCell(),
 | 
	
		
			
				|  |  | -              const VSpace(10),
 | 
	
		
			
				|  |  | -              _DeleteFieldButton(
 | 
	
		
			
				|  |  | -                popoverMutex: popoverMutex,
 | 
	
		
			
				|  |  | -                onDeleted: () {
 | 
	
		
			
				|  |  | -                  state.field.fold(
 | 
	
		
			
				|  |  | -                    () => Log.error('Can not delete the field'),
 | 
	
		
			
				|  |  | -                    (field) => widget.onDeleted?.call(field.id),
 | 
	
		
			
				|  |  | -                  );
 | 
	
		
			
				|  |  | -                },
 | 
	
		
			
				|  |  | +              FlowyText.medium(
 | 
	
		
			
				|  |  | +                LocaleKeys.grid_field_editProperty.tr(),
 | 
	
		
			
				|  |  | +                fontSize: 12,
 | 
	
		
			
				|  |  |                ),
 | 
	
		
			
				|  |  |                const VSpace(10),
 | 
	
		
			
				|  |  | +              _FieldNameTextField(popoverMutex: popoverMutex),
 | 
	
		
			
				|  |  | +              const VSpace(10),
 | 
	
		
			
				|  |  | +              ..._addDeleteFieldButton(state),
 | 
	
		
			
				|  |  |                _FieldTypeOptionCell(popoverMutex: popoverMutex),
 | 
	
		
			
				|  |  |              ],
 | 
	
		
			
				|  |  |            );
 | 
	
	
		
			
				|  | @@ -80,6 +79,23 @@ class _FieldEditorState extends State<FieldEditor> {
 | 
	
		
			
				|  |  |        ),
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  List<Widget> _addDeleteFieldButton(FieldEditorState state) {
 | 
	
		
			
				|  |  | +    if (widget.onDeleted == null) {
 | 
	
		
			
				|  |  | +      return [];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return [
 | 
	
		
			
				|  |  | +      _DeleteFieldButton(
 | 
	
		
			
				|  |  | +        popoverMutex: popoverMutex,
 | 
	
		
			
				|  |  | +        onDeleted: () {
 | 
	
		
			
				|  |  | +          state.field.fold(
 | 
	
		
			
				|  |  | +            () => Log.error('Can not delete the field'),
 | 
	
		
			
				|  |  | +            (field) => widget.onDeleted?.call(field.id),
 | 
	
		
			
				|  |  | +          );
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +      ),
 | 
	
		
			
				|  |  | +    ];
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class _FieldTypeOptionCell extends StatelessWidget {
 | 
	
	
		
			
				|  | @@ -111,25 +127,89 @@ class _FieldTypeOptionCell extends StatelessWidget {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class _FieldNameCell extends StatelessWidget {
 | 
	
		
			
				|  |  | -  const _FieldNameCell({Key? key}) : super(key: key);
 | 
	
		
			
				|  |  | +class _FieldNameTextField extends StatefulWidget {
 | 
	
		
			
				|  |  | +  final PopoverMutex popoverMutex;
 | 
	
		
			
				|  |  | +  const _FieldNameTextField({
 | 
	
		
			
				|  |  | +    required this.popoverMutex,
 | 
	
		
			
				|  |  | +    Key? key,
 | 
	
		
			
				|  |  | +  }) : super(key: key);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  @override
 | 
	
		
			
				|  |  | +  State<_FieldNameTextField> createState() => _FieldNameTextFieldState();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class _FieldNameTextFieldState extends State<_FieldNameTextField> {
 | 
	
		
			
				|  |  | +  late String name;
 | 
	
		
			
				|  |  | +  FocusNode focusNode = FocusNode();
 | 
	
		
			
				|  |  | +  VoidCallback? _popoverCallback;
 | 
	
		
			
				|  |  | +  TextEditingController controller = TextEditingController();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  @override
 | 
	
		
			
				|  |  | +  void initState() {
 | 
	
		
			
				|  |  | +    focusNode.addListener(() {
 | 
	
		
			
				|  |  | +      if (focusNode.hasFocus) {
 | 
	
		
			
				|  |  | +        widget.popoverMutex.close();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    super.initState();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    @override
 | 
	
		
			
				|  |  |    Widget build(BuildContext context) {
 | 
	
		
			
				|  |  | -    return BlocBuilder<FieldEditorBloc, FieldEditorState>(
 | 
	
		
			
				|  |  | -      builder: (context, state) {
 | 
	
		
			
				|  |  | -        return FieldNameTextField(
 | 
	
		
			
				|  |  | -          name: state.name,
 | 
	
		
			
				|  |  | -          errorText: context.read<FieldEditorBloc>().state.errorText,
 | 
	
		
			
				|  |  | -          onNameChanged: (newName) {
 | 
	
		
			
				|  |  | -            context
 | 
	
		
			
				|  |  | -                .read<FieldEditorBloc>()
 | 
	
		
			
				|  |  | -                .add(FieldEditorEvent.updateName(newName));
 | 
	
		
			
				|  |  | -          },
 | 
	
		
			
				|  |  | -        );
 | 
	
		
			
				|  |  | +    final theme = context.watch<AppTheme>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    controller.text = context.read<FieldEditorBloc>().state.name;
 | 
	
		
			
				|  |  | +    return BlocListener<FieldEditorBloc, FieldEditorState>(
 | 
	
		
			
				|  |  | +      listenWhen: (previous, current) => previous.name != current.name,
 | 
	
		
			
				|  |  | +      listener: (context, state) {
 | 
	
		
			
				|  |  | +        controller.text = state.name;
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  | +      child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
 | 
	
		
			
				|  |  | +        builder: (context, state) {
 | 
	
		
			
				|  |  | +          listenOnPopoverChhanged(context);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          return RoundedInputField(
 | 
	
		
			
				|  |  | +            height: 36,
 | 
	
		
			
				|  |  | +            autoFocus: true,
 | 
	
		
			
				|  |  | +            focusNode: focusNode,
 | 
	
		
			
				|  |  | +            style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
 | 
	
		
			
				|  |  | +            controller: controller,
 | 
	
		
			
				|  |  | +            normalBorderColor: theme.shader4,
 | 
	
		
			
				|  |  | +            errorBorderColor: theme.red,
 | 
	
		
			
				|  |  | +            focusBorderColor: theme.main1,
 | 
	
		
			
				|  |  | +            cursorColor: theme.main1,
 | 
	
		
			
				|  |  | +            errorText: context.read<FieldEditorBloc>().state.errorText,
 | 
	
		
			
				|  |  | +            onChanged: (newName) {
 | 
	
		
			
				|  |  | +              context
 | 
	
		
			
				|  |  | +                  .read<FieldEditorBloc>()
 | 
	
		
			
				|  |  | +                  .add(FieldEditorEvent.updateName(newName));
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +          );
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +      ),
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void listenOnPopoverChhanged(BuildContext context) {
 | 
	
		
			
				|  |  | +    if (_popoverCallback != null) {
 | 
	
		
			
				|  |  | +      widget.popoverMutex.removePopoverStateListener(_popoverCallback!);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    _popoverCallback = widget.popoverMutex.listenOnPopoverStateChanged(() {
 | 
	
		
			
				|  |  | +      if (focusNode.hasFocus) {
 | 
	
		
			
				|  |  | +        final node = FocusScope.of(context);
 | 
	
		
			
				|  |  | +        node.unfocus();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  @override
 | 
	
		
			
				|  |  | +  void didUpdateWidget(covariant _FieldNameTextField oldWidget) {
 | 
	
		
			
				|  |  | +    controller.selection = TextSelection.fromPosition(
 | 
	
		
			
				|  |  | +        TextPosition(offset: controller.text.length));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    super.didUpdateWidget(oldWidget);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class _DeleteFieldButton extends StatelessWidget {
 | 
	
	
		
			
				|  | @@ -171,12 +251,10 @@ class _DeleteFieldButton extends StatelessWidget {
 | 
	
		
			
				|  |  |        popupBuilder: (popupContext) {
 | 
	
		
			
				|  |  |          return PopoverAlertView(
 | 
	
		
			
				|  |  |            title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
 | 
	
		
			
				|  |  | -          cancel: () => popoverMutex.state?.close(),
 | 
	
		
			
				|  |  | +          cancel: () {},
 | 
	
		
			
				|  |  |            confirm: () {
 | 
	
		
			
				|  |  |              onDeleted?.call();
 | 
	
		
			
				|  |  | -            popoverMutex.state?.close();
 | 
	
		
			
				|  |  |            },
 | 
	
		
			
				|  |  | -          popoverMutex: popoverMutex,
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  |        child: widget,
 |