mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-11-04 12:03:28 +00:00 
			
		
		
		
	feat: checklist cell editor a11y improvement (#4784)
This commit is contained in:
		
							parent
							
								
									f4170755fa
								
							
						
					
					
						commit
						b38fc43300
					
				@ -11,7 +11,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
part 'checklist_cell_bloc.freezed.dart';
 | 
			
		||||
 | 
			
		||||
class ChecklistSelectOption {
 | 
			
		||||
  ChecklistSelectOption(this.isSelected, this.data);
 | 
			
		||||
  ChecklistSelectOption({required this.isSelected, required this.data});
 | 
			
		||||
 | 
			
		||||
  final bool isSelected;
 | 
			
		||||
  final SelectOptionPB data;
 | 
			
		||||
@ -26,6 +26,7 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
 | 
			
		||||
        ),
 | 
			
		||||
        super(ChecklistCellState.initial(cellController)) {
 | 
			
		||||
    _dispatch();
 | 
			
		||||
    _startListening();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final ChecklistCellController cellController;
 | 
			
		||||
@ -46,9 +47,6 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
 | 
			
		||||
    on<ChecklistCellEvent>(
 | 
			
		||||
      (event, emit) async {
 | 
			
		||||
        await event.when(
 | 
			
		||||
          initial: () {
 | 
			
		||||
            _startListening();
 | 
			
		||||
          },
 | 
			
		||||
          didReceiveOptions: (data) {
 | 
			
		||||
            if (data == null) {
 | 
			
		||||
              emit(
 | 
			
		||||
@ -71,8 +69,8 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
 | 
			
		||||
          updateTaskName: (option, name) {
 | 
			
		||||
            _updateOption(option, name);
 | 
			
		||||
          },
 | 
			
		||||
          selectTask: (option) async {
 | 
			
		||||
            await _checklistCellService.select(optionId: option.id);
 | 
			
		||||
          selectTask: (id) async {
 | 
			
		||||
            await _checklistCellService.select(optionId: id);
 | 
			
		||||
          },
 | 
			
		||||
          createNewTask: (name) async {
 | 
			
		||||
            final result = await _checklistCellService.create(name: name);
 | 
			
		||||
@ -81,8 +79,8 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
 | 
			
		||||
              (err) => Log.error(err),
 | 
			
		||||
            );
 | 
			
		||||
          },
 | 
			
		||||
          deleteTask: (option) async {
 | 
			
		||||
            await _deleteOption([option]);
 | 
			
		||||
          deleteTask: (id) async {
 | 
			
		||||
            await _deleteOption([id]);
 | 
			
		||||
          },
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
@ -102,21 +100,17 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
 | 
			
		||||
  void _updateOption(SelectOptionPB option, String name) async {
 | 
			
		||||
    final result =
 | 
			
		||||
        await _checklistCellService.updateName(option: option, name: name);
 | 
			
		||||
 | 
			
		||||
    result.fold((l) => null, (err) => Log.error(err));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _deleteOption(List<SelectOptionPB> options) async {
 | 
			
		||||
    final result = await _checklistCellService.delete(
 | 
			
		||||
      optionIds: options.map((e) => e.id).toList(),
 | 
			
		||||
    );
 | 
			
		||||
  Future<void> _deleteOption(List<String> options) async {
 | 
			
		||||
    final result = await _checklistCellService.delete(optionIds: options);
 | 
			
		||||
    result.fold((l) => null, (err) => Log.error(err));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
class ChecklistCellEvent with _$ChecklistCellEvent {
 | 
			
		||||
  const factory ChecklistCellEvent.initial() = _InitialCell;
 | 
			
		||||
  const factory ChecklistCellEvent.didReceiveOptions(
 | 
			
		||||
    ChecklistCellDataPB? data,
 | 
			
		||||
  ) = _DidReceiveCellUpdate;
 | 
			
		||||
@ -124,12 +118,10 @@ class ChecklistCellEvent with _$ChecklistCellEvent {
 | 
			
		||||
    SelectOptionPB option,
 | 
			
		||||
    String name,
 | 
			
		||||
  ) = _UpdateTaskName;
 | 
			
		||||
  const factory ChecklistCellEvent.selectTask(SelectOptionPB task) =
 | 
			
		||||
      _SelectTask;
 | 
			
		||||
  const factory ChecklistCellEvent.selectTask(String taskId) = _SelectTask;
 | 
			
		||||
  const factory ChecklistCellEvent.createNewTask(String description) =
 | 
			
		||||
      _CreateNewTask;
 | 
			
		||||
  const factory ChecklistCellEvent.deleteTask(SelectOptionPB option) =
 | 
			
		||||
      _DeleteTask;
 | 
			
		||||
  const factory ChecklistCellEvent.deleteTask(String taskId) = _DeleteTask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
@ -157,16 +149,14 @@ List<ChecklistSelectOption> _makeChecklistSelectOptions(
 | 
			
		||||
  if (data == null) {
 | 
			
		||||
    return [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final List<ChecklistSelectOption> options = [];
 | 
			
		||||
  final List<SelectOptionPB> allOptions = List.from(data.options);
 | 
			
		||||
  final selectedOptionIds = data.selectedOptions.map((e) => e.id).toList();
 | 
			
		||||
 | 
			
		||||
  for (final option in allOptions) {
 | 
			
		||||
    options.add(
 | 
			
		||||
      ChecklistSelectOption(selectedOptionIds.contains(option.id), option),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return options;
 | 
			
		||||
  return data.options
 | 
			
		||||
      .map(
 | 
			
		||||
        (option) => ChecklistSelectOption(
 | 
			
		||||
          isSelected: data.selectedOptions.any(
 | 
			
		||||
            (selected) => selected.id == option.id,
 | 
			
		||||
          ),
 | 
			
		||||
          data: option,
 | 
			
		||||
        ),
 | 
			
		||||
      )
 | 
			
		||||
      .toList();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ class _ChecklistCellState extends State<ChecklistCardCell> {
 | 
			
		||||
            widget.databaseController,
 | 
			
		||||
            widget.cellContext,
 | 
			
		||||
          ).as(),
 | 
			
		||||
        )..add(const ChecklistCellEvent.initial());
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
      child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
 | 
			
		||||
        builder: (context, state) {
 | 
			
		||||
 | 
			
		||||
@ -64,7 +64,9 @@ class _ChecklistItemsState extends State<ChecklistItems> {
 | 
			
		||||
    }
 | 
			
		||||
    final children = tasks
 | 
			
		||||
        .mapIndexed(
 | 
			
		||||
          (index, task) => ChecklistItem(
 | 
			
		||||
          (index, task) => Padding(
 | 
			
		||||
            padding: const EdgeInsets.symmetric(vertical: 2.0),
 | 
			
		||||
            child: ChecklistItem(
 | 
			
		||||
              task: task,
 | 
			
		||||
              autofocus: widget.state.newTask && index == tasks.length - 1,
 | 
			
		||||
              onSubmitted: () {
 | 
			
		||||
@ -73,6 +75,7 @@ class _ChecklistItemsState extends State<ChecklistItems> {
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        )
 | 
			
		||||
        .toList();
 | 
			
		||||
    return Align(
 | 
			
		||||
@ -111,7 +114,7 @@ class _ChecklistItemsState extends State<ChecklistItems> {
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const VSpace(4),
 | 
			
		||||
          const VSpace(2.0),
 | 
			
		||||
          ...children,
 | 
			
		||||
          ChecklistItemControl(cellNotifer: widget.cellContainerNotifier),
 | 
			
		||||
        ],
 | 
			
		||||
@ -136,7 +139,7 @@ class ChecklistItemControl extends StatelessWidget {
 | 
			
		||||
              .read<ChecklistCellBloc>()
 | 
			
		||||
              .add(const ChecklistCellEvent.createNewTask("")),
 | 
			
		||||
          child: Container(
 | 
			
		||||
            margin: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
 | 
			
		||||
            margin: const EdgeInsets.fromLTRB(8.0, 2.0, 8.0, 0),
 | 
			
		||||
            height: 12,
 | 
			
		||||
            child: AnimatedSwitcher(
 | 
			
		||||
              duration: const Duration(milliseconds: 150),
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,7 @@ class GridChecklistCellState extends GridCellState<EditableChecklistCell> {
 | 
			
		||||
      widget.databaseController,
 | 
			
		||||
      widget.cellContext,
 | 
			
		||||
    ).as(),
 | 
			
		||||
  )..add(const ChecklistCellEvent.initial());
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
 | 
			
		||||
import 'package:appflowy/generated/locale_keys.g.dart';
 | 
			
		||||
@ -23,10 +24,10 @@ class ChecklistCellEditor extends StatefulWidget {
 | 
			
		||||
  final ChecklistCellController cellController;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<ChecklistCellEditor> createState() => _GridChecklistCellState();
 | 
			
		||||
  State<ChecklistCellEditor> createState() => _ChecklistCellEditorState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _GridChecklistCellState extends State<ChecklistCellEditor> {
 | 
			
		||||
class _ChecklistCellEditorState extends State<ChecklistCellEditor> {
 | 
			
		||||
  /// Focus node for the new task text field
 | 
			
		||||
  late final FocusNode newTaskFocusNode;
 | 
			
		||||
 | 
			
		||||
@ -56,18 +57,14 @@ class _GridChecklistCellState extends State<ChecklistCellEditor> {
 | 
			
		||||
        return Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: [
 | 
			
		||||
            AnimatedSwitcher(
 | 
			
		||||
              duration: const Duration(milliseconds: 300),
 | 
			
		||||
              child: state.tasks.isEmpty
 | 
			
		||||
                  ? const SizedBox.shrink()
 | 
			
		||||
                  : Padding(
 | 
			
		||||
            if (state.tasks.isNotEmpty)
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: const EdgeInsets.fromLTRB(16, 16, 16, 4),
 | 
			
		||||
                child: ChecklistProgressBar(
 | 
			
		||||
                  tasks: state.tasks,
 | 
			
		||||
                  percent: state.percent,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            ChecklistItemList(
 | 
			
		||||
              options: state.tasks,
 | 
			
		||||
              onUpdateTask: () => newTaskFocusNode.requestFocus(),
 | 
			
		||||
@ -92,7 +89,7 @@ class _GridChecklistCellState extends State<ChecklistCellEditor> {
 | 
			
		||||
 | 
			
		||||
/// Displays the a list of all the exisiting tasks and an input field to create
 | 
			
		||||
/// a new task if `isAddingNewTask` is true
 | 
			
		||||
class ChecklistItemList extends StatefulWidget {
 | 
			
		||||
class ChecklistItemList extends StatelessWidget {
 | 
			
		||||
  const ChecklistItemList({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.options,
 | 
			
		||||
@ -102,26 +99,19 @@ class ChecklistItemList extends StatefulWidget {
 | 
			
		||||
  final List<ChecklistSelectOption> options;
 | 
			
		||||
  final VoidCallback onUpdateTask;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<ChecklistItemList> createState() => _ChecklistItemListState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ChecklistItemListState extends State<ChecklistItemList> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (widget.options.isEmpty) {
 | 
			
		||||
    if (options.isEmpty) {
 | 
			
		||||
      return const SizedBox.shrink();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final itemList = widget.options
 | 
			
		||||
    final itemList = options
 | 
			
		||||
        .mapIndexed(
 | 
			
		||||
          (index, option) => Padding(
 | 
			
		||||
            padding: const EdgeInsets.symmetric(horizontal: 8.0),
 | 
			
		||||
            child: ChecklistItem(
 | 
			
		||||
              task: option,
 | 
			
		||||
              onSubmitted: index == widget.options.length - 1
 | 
			
		||||
                  ? widget.onUpdateTask
 | 
			
		||||
                  : null,
 | 
			
		||||
              onSubmitted: index == options.length - 1 ? onUpdateTask : null,
 | 
			
		||||
              key: ValueKey(option.data.id),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
@ -140,6 +130,22 @@ class _ChecklistItemListState extends State<ChecklistItemList> {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _SelectTaskIntent extends Intent {
 | 
			
		||||
  const _SelectTaskIntent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _DeleteTaskIntent extends Intent {
 | 
			
		||||
  const _DeleteTaskIntent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _StartEditingTaskIntent extends Intent {
 | 
			
		||||
  const _StartEditingTaskIntent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _EndEditingTaskIntent extends Intent {
 | 
			
		||||
  const _EndEditingTaskIntent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents an existing task
 | 
			
		||||
@visibleForTesting
 | 
			
		||||
class ChecklistItem extends StatefulWidget {
 | 
			
		||||
@ -160,58 +166,80 @@ class ChecklistItem extends StatefulWidget {
 | 
			
		||||
 | 
			
		||||
class _ChecklistItemState extends State<ChecklistItem> {
 | 
			
		||||
  late final TextEditingController _textController;
 | 
			
		||||
  late final FocusNode _focusNode;
 | 
			
		||||
  final FocusNode _focusNode = FocusNode();
 | 
			
		||||
  bool _isHovered = false;
 | 
			
		||||
  bool _isFocused = false;
 | 
			
		||||
  Timer? _debounceOnChanged;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _textController = TextEditingController(text: widget.task.data.name);
 | 
			
		||||
    _focusNode = FocusNode(
 | 
			
		||||
      onKeyEvent: (node, event) {
 | 
			
		||||
        if (event.logicalKey == LogicalKeyboardKey.escape) {
 | 
			
		||||
          node.unfocus();
 | 
			
		||||
          return KeyEventResult.handled;
 | 
			
		||||
        }
 | 
			
		||||
        return KeyEventResult.ignored;
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    if (widget.autofocus) {
 | 
			
		||||
      _focusNode.requestFocus();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    _debounceOnChanged?.cancel();
 | 
			
		||||
    _textController.dispose();
 | 
			
		||||
    _focusNode.dispose();
 | 
			
		||||
    _debounceOnChanged?.cancel();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void didUpdateWidget(ChecklistItem oldWidget) {
 | 
			
		||||
    super.didUpdateWidget(oldWidget);
 | 
			
		||||
    if (widget.task.data.name != oldWidget.task.data.name &&
 | 
			
		||||
        !_focusNode.hasFocus) {
 | 
			
		||||
    if (widget.task.data.name != oldWidget.task.data.name) {
 | 
			
		||||
      _textController.text = widget.task.data.name;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final icon = FlowySvg(
 | 
			
		||||
      widget.task.isSelected ? FlowySvgs.check_filled_s : FlowySvgs.uncheck_s,
 | 
			
		||||
      blendMode: BlendMode.dst,
 | 
			
		||||
    );
 | 
			
		||||
    return MouseRegion(
 | 
			
		||||
      onEnter: (event) => setState(() => _isHovered = true),
 | 
			
		||||
      onExit: (event) => setState(() => _isHovered = false),
 | 
			
		||||
    return FocusableActionDetector(
 | 
			
		||||
      onShowHoverHighlight: (isHovered) {
 | 
			
		||||
        setState(() => _isHovered = isHovered);
 | 
			
		||||
      },
 | 
			
		||||
      onFocusChange: (isFocused) {
 | 
			
		||||
        setState(() => _isFocused = isFocused);
 | 
			
		||||
      },
 | 
			
		||||
      actions: {
 | 
			
		||||
        _SelectTaskIntent: CallbackAction<_SelectTaskIntent>(
 | 
			
		||||
          onInvoke: (_SelectTaskIntent intent) => context
 | 
			
		||||
              .read<ChecklistCellBloc>()
 | 
			
		||||
              .add(ChecklistCellEvent.selectTask(widget.task.data.id)),
 | 
			
		||||
        ),
 | 
			
		||||
        _DeleteTaskIntent: CallbackAction<_DeleteTaskIntent>(
 | 
			
		||||
          onInvoke: (_DeleteTaskIntent intent) => context
 | 
			
		||||
              .read<ChecklistCellBloc>()
 | 
			
		||||
              .add(ChecklistCellEvent.deleteTask(widget.task.data.id)),
 | 
			
		||||
        ),
 | 
			
		||||
        _StartEditingTaskIntent: CallbackAction<_StartEditingTaskIntent>(
 | 
			
		||||
          onInvoke: (_StartEditingTaskIntent intent) =>
 | 
			
		||||
              _focusNode.requestFocus(),
 | 
			
		||||
        ),
 | 
			
		||||
        _EndEditingTaskIntent: CallbackAction<_EndEditingTaskIntent>(
 | 
			
		||||
          onInvoke: (_EndEditingTaskIntent intent) => _focusNode.unfocus(),
 | 
			
		||||
        ),
 | 
			
		||||
      },
 | 
			
		||||
      shortcuts: {
 | 
			
		||||
        const SingleActivator(LogicalKeyboardKey.space):
 | 
			
		||||
            const _SelectTaskIntent(),
 | 
			
		||||
        const SingleActivator(LogicalKeyboardKey.delete):
 | 
			
		||||
            const _DeleteTaskIntent(),
 | 
			
		||||
        const SingleActivator(LogicalKeyboardKey.enter):
 | 
			
		||||
            const _StartEditingTaskIntent(),
 | 
			
		||||
        if (Platform.isMacOS)
 | 
			
		||||
          const SingleActivator(LogicalKeyboardKey.enter, meta: true):
 | 
			
		||||
              const _SelectTaskIntent()
 | 
			
		||||
        else
 | 
			
		||||
          const SingleActivator(LogicalKeyboardKey.enter, control: true):
 | 
			
		||||
              const _SelectTaskIntent(),
 | 
			
		||||
      },
 | 
			
		||||
      descendantsAreTraversable: false,
 | 
			
		||||
      child: Container(
 | 
			
		||||
        constraints: BoxConstraints(minHeight: GridSize.popoverItemHeight),
 | 
			
		||||
        decoration: BoxDecoration(
 | 
			
		||||
          color: _isHovered
 | 
			
		||||
          color: _isHovered || _isFocused || _focusNode.hasFocus
 | 
			
		||||
              ? AFThemeExtension.of(context).lightGreyHover
 | 
			
		||||
              : Colors.transparent,
 | 
			
		||||
          borderRadius: Corners.s6Border,
 | 
			
		||||
@ -220,16 +248,33 @@ class _ChecklistItemState extends State<ChecklistItem> {
 | 
			
		||||
          children: [
 | 
			
		||||
            FlowyIconButton(
 | 
			
		||||
              width: 32,
 | 
			
		||||
              icon: icon,
 | 
			
		||||
              icon: FlowySvg(
 | 
			
		||||
                widget.task.isSelected
 | 
			
		||||
                    ? FlowySvgs.check_filled_s
 | 
			
		||||
                    : FlowySvgs.uncheck_s,
 | 
			
		||||
                blendMode: BlendMode.dst,
 | 
			
		||||
              ),
 | 
			
		||||
              hoverColor: Colors.transparent,
 | 
			
		||||
              onPressed: () => context.read<ChecklistCellBloc>().add(
 | 
			
		||||
                    ChecklistCellEvent.selectTask(widget.task.data),
 | 
			
		||||
                    ChecklistCellEvent.selectTask(widget.task.data.id),
 | 
			
		||||
                  ),
 | 
			
		||||
            ),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: Shortcuts(
 | 
			
		||||
                shortcuts: const {
 | 
			
		||||
                  SingleActivator(LogicalKeyboardKey.space):
 | 
			
		||||
                      DoNothingAndStopPropagationIntent(),
 | 
			
		||||
                  SingleActivator(LogicalKeyboardKey.delete):
 | 
			
		||||
                      DoNothingAndStopPropagationIntent(),
 | 
			
		||||
                  SingleActivator(LogicalKeyboardKey.enter):
 | 
			
		||||
                      DoNothingAndStopPropagationIntent(),
 | 
			
		||||
                  SingleActivator(LogicalKeyboardKey.escape):
 | 
			
		||||
                      _EndEditingTaskIntent(),
 | 
			
		||||
                },
 | 
			
		||||
                child: TextField(
 | 
			
		||||
                  controller: _textController,
 | 
			
		||||
                  focusNode: _focusNode,
 | 
			
		||||
                  autofocus: widget.autofocus,
 | 
			
		||||
                  style: Theme.of(context).textTheme.bodyMedium,
 | 
			
		||||
                  decoration: InputDecoration(
 | 
			
		||||
                    border: InputBorder.none,
 | 
			
		||||
@ -242,21 +287,26 @@ class _ChecklistItemState extends State<ChecklistItem> {
 | 
			
		||||
                    ),
 | 
			
		||||
                    hintText: LocaleKeys.grid_checklist_taskHint.tr(),
 | 
			
		||||
                  ),
 | 
			
		||||
                onChanged: _debounceOnChangedText,
 | 
			
		||||
                  onChanged: (text) {
 | 
			
		||||
                    if (_textController.value.composing.isCollapsed) {
 | 
			
		||||
                      _debounceOnChangedText(text);
 | 
			
		||||
                    }
 | 
			
		||||
                  },
 | 
			
		||||
                  onSubmitted: (description) {
 | 
			
		||||
                    _submitUpdateTaskDescription(description);
 | 
			
		||||
                    widget.onSubmitted?.call();
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            if (_isHovered)
 | 
			
		||||
            ),
 | 
			
		||||
            if (_isHovered || _isFocused || _focusNode.hasFocus)
 | 
			
		||||
              FlowyIconButton(
 | 
			
		||||
                width: 32,
 | 
			
		||||
                icon: const FlowySvg(FlowySvgs.delete_s),
 | 
			
		||||
                hoverColor: Colors.transparent,
 | 
			
		||||
                iconColorOnHover: Theme.of(context).colorScheme.error,
 | 
			
		||||
                onPressed: () => context.read<ChecklistCellBloc>().add(
 | 
			
		||||
                      ChecklistCellEvent.deleteTask(widget.task.data),
 | 
			
		||||
                      ChecklistCellEvent.deleteTask(widget.task.data.id),
 | 
			
		||||
                    ),
 | 
			
		||||
              ),
 | 
			
		||||
          ],
 | 
			
		||||
@ -276,7 +326,7 @@ class _ChecklistItemState extends State<ChecklistItem> {
 | 
			
		||||
    context.read<ChecklistCellBloc>().add(
 | 
			
		||||
          ChecklistCellEvent.updateTaskName(
 | 
			
		||||
            widget.task.data,
 | 
			
		||||
            description.trim(),
 | 
			
		||||
            description,
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -159,7 +159,7 @@ class _ChecklistItemState extends State<_ChecklistItem> {
 | 
			
		||||
            borderRadius: BorderRadius.circular(22),
 | 
			
		||||
            onTap: () => context
 | 
			
		||||
                .read<ChecklistCellBloc>()
 | 
			
		||||
                .add(ChecklistCellEvent.selectTask(widget.task.data)),
 | 
			
		||||
                .add(ChecklistCellEvent.selectTask(widget.task.data.id)),
 | 
			
		||||
            child: SizedBox.square(
 | 
			
		||||
              dimension: 44,
 | 
			
		||||
              child: Center(
 | 
			
		||||
@ -239,7 +239,7 @@ class _ChecklistItemState extends State<_ChecklistItem> {
 | 
			
		||||
            child: InkWell(
 | 
			
		||||
              onTap: () {
 | 
			
		||||
                context.read<ChecklistCellBloc>().add(
 | 
			
		||||
                      ChecklistCellEvent.deleteTask(widget.task.data),
 | 
			
		||||
                      ChecklistCellEvent.deleteTask(widget.task.data.id),
 | 
			
		||||
                    );
 | 
			
		||||
                context.pop();
 | 
			
		||||
              },
 | 
			
		||||
 | 
			
		||||
@ -161,15 +161,16 @@ class PopoverState extends State<Popover> {
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return FocusScope(
 | 
			
		||||
        onKey: (node, event) {
 | 
			
		||||
          if (event.logicalKey == LogicalKeyboardKey.escape) {
 | 
			
		||||
            _removeRootOverlay();
 | 
			
		||||
            return KeyEventResult.handled;
 | 
			
		||||
          }
 | 
			
		||||
          return KeyEventResult.ignored;
 | 
			
		||||
      return CallbackShortcuts(
 | 
			
		||||
        bindings: {
 | 
			
		||||
          const SingleActivator(LogicalKeyboardKey.escape): () =>
 | 
			
		||||
              _removeRootOverlay(),
 | 
			
		||||
        },
 | 
			
		||||
        child: Stack(children: children),
 | 
			
		||||
        child: FocusScope(
 | 
			
		||||
          child: Stack(
 | 
			
		||||
            children: children,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
    _rootEntry.addEntry(context, this, newEntry, widget.asBarrier);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user