mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-24 22:57:12 -04:00
fix: checklist cell did get notified after the cell content change
This commit is contained in:
parent
3cdd6665b3
commit
29e07089ca
12 changed files with 195 additions and 90 deletions
|
@ -51,7 +51,11 @@ class ChecklistCellBloc extends Bloc<ChecklistCellEvent, ChecklistCellState> {
|
||||||
onCellFieldChanged: () {
|
onCellFieldChanged: () {
|
||||||
_loadOptions();
|
_loadOptions();
|
||||||
},
|
},
|
||||||
onCellChanged: (_) {},
|
onCellChanged: (data) {
|
||||||
|
if (!isClosed && data != null) {
|
||||||
|
add(ChecklistCellEvent.didReceiveOptions(data));
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ class ChecklistCellEditorBloc
|
||||||
extends Bloc<ChecklistCellEditorEvent, ChecklistCellEditorState> {
|
extends Bloc<ChecklistCellEditorEvent, ChecklistCellEditorState> {
|
||||||
final SelectOptionFFIService _selectOptionService;
|
final SelectOptionFFIService _selectOptionService;
|
||||||
final GridChecklistCellController cellController;
|
final GridChecklistCellController cellController;
|
||||||
Timer? _delayOperation;
|
|
||||||
|
|
||||||
ChecklistCellEditorBloc({
|
ChecklistCellEditorBloc({
|
||||||
required this.cellController,
|
required this.cellController,
|
||||||
|
@ -27,7 +26,6 @@ class ChecklistCellEditorBloc
|
||||||
await event.when(
|
await event.when(
|
||||||
initial: () async {
|
initial: () async {
|
||||||
_startListening();
|
_startListening();
|
||||||
_loadOptions();
|
|
||||||
},
|
},
|
||||||
didReceiveOptions: (data) {
|
didReceiveOptions: (data) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
|
@ -47,11 +45,12 @@ class ChecklistCellEditorBloc
|
||||||
updateOption: (option) {
|
updateOption: (option) {
|
||||||
_updateOption(option);
|
_updateOption(option);
|
||||||
},
|
},
|
||||||
selectOption: (optionId) {
|
selectOption: (option) async {
|
||||||
_selectOptionService.select(optionIds: [optionId]);
|
if (option.isSelected) {
|
||||||
},
|
await _selectOptionService.unSelect(optionIds: [option.data.id]);
|
||||||
unSelectOption: (optionId) {
|
} else {
|
||||||
_selectOptionService.unSelect(optionIds: [optionId]);
|
await _selectOptionService.select(optionIds: [option.data.id]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
filterOption: (String predicate) {},
|
filterOption: (String predicate) {},
|
||||||
);
|
);
|
||||||
|
@ -61,7 +60,6 @@ class ChecklistCellEditorBloc
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
_delayOperation?.cancel();
|
|
||||||
await cellController.dispose();
|
await cellController.dispose();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
@ -119,10 +117,8 @@ class ChecklistCellEditorEvent with _$ChecklistCellEditorEvent {
|
||||||
SelectOptionCellDataPB data) = _DidReceiveOptions;
|
SelectOptionCellDataPB data) = _DidReceiveOptions;
|
||||||
const factory ChecklistCellEditorEvent.newOption(String optionName) =
|
const factory ChecklistCellEditorEvent.newOption(String optionName) =
|
||||||
_NewOption;
|
_NewOption;
|
||||||
const factory ChecklistCellEditorEvent.selectOption(String optionId) =
|
const factory ChecklistCellEditorEvent.selectOption(
|
||||||
_SelectOption;
|
ChecklistSelectOption option) = _SelectOption;
|
||||||
const factory ChecklistCellEditorEvent.unSelectOption(String optionId) =
|
|
||||||
_UnSelectOption;
|
|
||||||
const factory ChecklistCellEditorEvent.updateOption(SelectOptionPB option) =
|
const factory ChecklistCellEditorEvent.updateOption(SelectOptionPB option) =
|
||||||
_UpdateOption;
|
_UpdateOption;
|
||||||
const factory ChecklistCellEditorEvent.deleteOption(SelectOptionPB option) =
|
const factory ChecklistCellEditorEvent.deleteOption(SelectOptionPB option) =
|
||||||
|
|
|
@ -522,6 +522,7 @@ class FieldInfo {
|
||||||
case FieldType.MultiSelect:
|
case FieldType.MultiSelect:
|
||||||
case FieldType.RichText:
|
case FieldType.RichText:
|
||||||
case FieldType.SingleSelect:
|
case FieldType.SingleSelect:
|
||||||
|
// case FieldType.Checklist:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -36,20 +36,16 @@ class GridChecklistCellState extends State<GridChecklistCell> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: _cellBloc,
|
value: _cellBloc,
|
||||||
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
|
child: Stack(
|
||||||
builder: (context, state) {
|
alignment: AlignmentDirectional.center,
|
||||||
return Stack(
|
fit: StackFit.expand,
|
||||||
alignment: AlignmentDirectional.center,
|
children: [
|
||||||
fit: StackFit.expand,
|
Padding(
|
||||||
children: [
|
padding: GridSize.cellContentInsets,
|
||||||
Padding(
|
child: _wrapPopover(const ChecklistProgressBar()),
|
||||||
padding: GridSize.cellContentInsets,
|
),
|
||||||
child: _wrapPopover(const ChecklistProgressBar()),
|
InkWell(onTap: () => _popover.show()),
|
||||||
),
|
],
|
||||||
InkWell(onTap: () => _popover.show()),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/header/type_option/s
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.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/icon_button.dart';
|
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
|
@ -24,9 +25,11 @@ class GridChecklistCellEditor extends StatefulWidget {
|
||||||
|
|
||||||
class _GridChecklistCellEditorState extends State<GridChecklistCellEditor> {
|
class _GridChecklistCellEditorState extends State<GridChecklistCellEditor> {
|
||||||
late ChecklistCellEditorBloc bloc;
|
late ChecklistCellEditorBloc bloc;
|
||||||
|
late PopoverMutex popoverMutex;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
popoverMutex = PopoverMutex();
|
||||||
bloc = ChecklistCellEditorBloc(cellController: widget.cellController);
|
bloc = ChecklistCellEditorBloc(cellController: widget.cellController);
|
||||||
bloc.add(const ChecklistCellEditorEvent.initial());
|
bloc.add(const ChecklistCellEditorEvent.initial());
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -47,23 +50,28 @@ class _GridChecklistCellEditorState extends State<GridChecklistCellEditor> {
|
||||||
final List<Widget> slivers = [
|
final List<Widget> slivers = [
|
||||||
const SliverChecklistPrograssBar(),
|
const SliverChecklistPrograssBar(),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(color: Colors.red, height: 2, width: 2100)),
|
child: Padding(
|
||||||
SliverToBoxAdapter(
|
padding: GridSize.typeOptionContentInsets,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
controller: ScrollController(),
|
controller: ScrollController(),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: state.allOptions.length,
|
itemCount: state.allOptions.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return _ChecklistOptionCell(option: state.allOptions[index]);
|
return _ChecklistOptionCell(
|
||||||
},
|
option: state.allOptions[index],
|
||||||
separatorBuilder: (BuildContext context, int index) {
|
popoverMutex: popoverMutex,
|
||||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
);
|
||||||
},
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
return ScrollConfiguration(
|
||||||
|
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
slivers: slivers,
|
slivers: slivers,
|
||||||
|
@ -79,8 +87,10 @@ class _GridChecklistCellEditorState extends State<GridChecklistCellEditor> {
|
||||||
|
|
||||||
class _ChecklistOptionCell extends StatefulWidget {
|
class _ChecklistOptionCell extends StatefulWidget {
|
||||||
final ChecklistSelectOption option;
|
final ChecklistSelectOption option;
|
||||||
|
final PopoverMutex popoverMutex;
|
||||||
const _ChecklistOptionCell({
|
const _ChecklistOptionCell({
|
||||||
required this.option,
|
required this.option,
|
||||||
|
required this.popoverMutex,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@ -107,10 +117,15 @@ class _ChecklistOptionCellState extends State<_ChecklistOptionCell> {
|
||||||
height: GridSize.typeOptionItemHeight,
|
height: GridSize.typeOptionItemHeight,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
icon,
|
Expanded(
|
||||||
const HSpace(6),
|
child: FlowyButton(
|
||||||
FlowyText(widget.option.data.name),
|
text: FlowyText(widget.option.data.name),
|
||||||
const Spacer(),
|
leftIcon: icon,
|
||||||
|
onTap: () => context
|
||||||
|
.read<ChecklistCellEditorBloc>()
|
||||||
|
.add(ChecklistCellEditorEvent.selectOption(widget.option)),
|
||||||
|
),
|
||||||
|
),
|
||||||
_disclosureButton(),
|
_disclosureButton(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -122,8 +137,7 @@ class _ChecklistOptionCellState extends State<_ChecklistOptionCell> {
|
||||||
return FlowyIconButton(
|
return FlowyIconButton(
|
||||||
width: 20,
|
width: 20,
|
||||||
onPressed: () => _popoverController.show(),
|
onPressed: () => _popoverController.show(),
|
||||||
hoverColor: Colors.transparent,
|
iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2),
|
||||||
iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
|
||||||
icon: svgWidget(
|
icon: svgWidget(
|
||||||
"editor/details",
|
"editor/details",
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
@ -137,15 +151,30 @@ class _ChecklistOptionCellState extends State<_ChecklistOptionCell> {
|
||||||
offset: const Offset(20, 0),
|
offset: const Offset(20, 0),
|
||||||
asBarrier: true,
|
asBarrier: true,
|
||||||
constraints: BoxConstraints.loose(const Size(200, 300)),
|
constraints: BoxConstraints.loose(const Size(200, 300)),
|
||||||
|
mutex: widget.popoverMutex,
|
||||||
|
triggerActions: PopoverTriggerFlags.none,
|
||||||
child: child,
|
child: child,
|
||||||
popupBuilder: (BuildContext popoverContext) {
|
popupBuilder: (BuildContext popoverContext) {
|
||||||
return SelectOptionTypeOptionEditor(
|
return SelectOptionTypeOptionEditor(
|
||||||
option: widget.option.data,
|
option: widget.option.data,
|
||||||
onDeleted: () {},
|
onDeleted: () {
|
||||||
onUpdated: (updatedOption) {},
|
context.read<ChecklistCellEditorBloc>().add(
|
||||||
|
ChecklistCellEditorEvent.deleteOption(widget.option.data),
|
||||||
|
);
|
||||||
|
|
||||||
|
_popoverController.close();
|
||||||
|
},
|
||||||
|
onUpdated: (updatedOption) {
|
||||||
|
context.read<ChecklistCellEditorBloc>().add(
|
||||||
|
ChecklistCellEditorEvent.updateOption(widget.option.data),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
showOptions: false,
|
||||||
|
autoFocus: false,
|
||||||
|
// Use ValueKey to refresh the UI, otherwise, it will remain the old value.
|
||||||
key: ValueKey(
|
key: ValueKey(
|
||||||
widget.option.data.id,
|
widget.option.data.id,
|
||||||
), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||||
import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_editor_bloc.dart';
|
import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_editor_bloc.dart';
|
||||||
|
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/color_extension.dart';
|
import 'package:flowy_infra/color_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
@ -41,34 +42,39 @@ class _SliverChecklistPrograssBarDelegate
|
||||||
extends SliverPersistentHeaderDelegate {
|
extends SliverPersistentHeaderDelegate {
|
||||||
_SliverChecklistPrograssBarDelegate();
|
_SliverChecklistPrograssBarDelegate();
|
||||||
|
|
||||||
double fixHeight = 80;
|
double fixHeight = 60;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(
|
Widget build(
|
||||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||||
return BlocBuilder<ChecklistCellEditorBloc, ChecklistCellEditorState>(
|
return BlocBuilder<ChecklistCellEditorBloc, ChecklistCellEditorState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Column(
|
return Container(
|
||||||
children: [
|
color: Theme.of(context).colorScheme.background,
|
||||||
if (state.percent != 0)
|
padding: GridSize.typeOptionContentInsets,
|
||||||
Padding(
|
child: Column(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
children: [
|
||||||
child: ChecklistPrograssBar(percent: state.percent),
|
FlowyTextField(
|
||||||
|
autoClearWhenDone: true,
|
||||||
|
hintText: LocaleKeys.grid_checklist_panelTitle.tr(),
|
||||||
|
onChanged: (text) {
|
||||||
|
context
|
||||||
|
.read<ChecklistCellEditorBloc>()
|
||||||
|
.add(ChecklistCellEditorEvent.filterOption(text));
|
||||||
|
},
|
||||||
|
onSubmitted: (text) {
|
||||||
|
context
|
||||||
|
.read<ChecklistCellEditorBloc>()
|
||||||
|
.add(ChecklistCellEditorEvent.newOption(text));
|
||||||
|
},
|
||||||
),
|
),
|
||||||
FlowyTextField(
|
if (state.percent != 0)
|
||||||
hintText: LocaleKeys.grid_checklist_panelTitle.tr(),
|
Padding(
|
||||||
onChanged: (text) {
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
context
|
child: ChecklistPrograssBar(percent: state.percent),
|
||||||
.read<ChecklistCellEditorBloc>()
|
),
|
||||||
.add(ChecklistCellEditorEvent.filterOption(text));
|
],
|
||||||
},
|
),
|
||||||
onSubmitted: (text) {
|
|
||||||
context
|
|
||||||
.read<ChecklistCellEditorBloc>()
|
|
||||||
.add(ChecklistCellEditorEvent.newOption(text));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flowy_infra/image.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -18,10 +19,14 @@ class SelectOptionTypeOptionEditor extends StatelessWidget {
|
||||||
final SelectOptionPB option;
|
final SelectOptionPB option;
|
||||||
final VoidCallback onDeleted;
|
final VoidCallback onDeleted;
|
||||||
final Function(SelectOptionPB) onUpdated;
|
final Function(SelectOptionPB) onUpdated;
|
||||||
|
final bool showOptions;
|
||||||
|
final bool autoFocus;
|
||||||
const SelectOptionTypeOptionEditor({
|
const SelectOptionTypeOptionEditor({
|
||||||
required this.option,
|
required this.option,
|
||||||
required this.onDeleted,
|
required this.onDeleted,
|
||||||
required this.onUpdated,
|
required this.onUpdated,
|
||||||
|
this.showOptions = true,
|
||||||
|
this.autoFocus = true,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@ -50,21 +55,29 @@ class SelectOptionTypeOptionEditor extends StatelessWidget {
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
List<Widget> slivers = [
|
List<Widget> slivers = [
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: _OptionNameTextField(state.option.name)),
|
child: _OptionNameTextField(
|
||||||
|
name: state.option.name,
|
||||||
|
autoFocus: autoFocus,
|
||||||
|
)),
|
||||||
const SliverToBoxAdapter(child: VSpace(10)),
|
const SliverToBoxAdapter(child: VSpace(10)),
|
||||||
const SliverToBoxAdapter(child: _DeleteTag()),
|
const SliverToBoxAdapter(child: _DeleteTag()),
|
||||||
const SliverToBoxAdapter(child: TypeOptionSeparator()),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child:
|
|
||||||
SelectOptionColorList(selectedColor: state.option.color)),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (showOptions) {
|
||||||
|
slivers
|
||||||
|
.add(const SliverToBoxAdapter(child: TypeOptionSeparator()));
|
||||||
|
slivers.add(SliverToBoxAdapter(
|
||||||
|
child: SelectOptionColorList(
|
||||||
|
selectedColor: state.option.color)));
|
||||||
|
}
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 160,
|
width: 160,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(6.0),
|
padding: const EdgeInsets.all(6.0),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: slivers,
|
slivers: slivers,
|
||||||
|
shrinkWrap: true,
|
||||||
controller: ScrollController(),
|
controller: ScrollController(),
|
||||||
physics: StyledScrollPhysics(),
|
physics: StyledScrollPhysics(),
|
||||||
),
|
),
|
||||||
|
@ -102,19 +115,21 @@ class _DeleteTag extends StatelessWidget {
|
||||||
|
|
||||||
class _OptionNameTextField extends StatelessWidget {
|
class _OptionNameTextField extends StatelessWidget {
|
||||||
final String name;
|
final String name;
|
||||||
const _OptionNameTextField(this.name, {Key? key}) : super(key: key);
|
final bool autoFocus;
|
||||||
|
const _OptionNameTextField(
|
||||||
|
{required this.name, required this.autoFocus, Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InputTextField(
|
return FlowyTextField(
|
||||||
|
autoFucous: autoFocus,
|
||||||
text: name,
|
text: name,
|
||||||
maxLength: 30,
|
onSubmitted: (newName) {
|
||||||
onCanceled: () {},
|
if (name != newName) {
|
||||||
onDone: (optionName) {
|
|
||||||
if (name != optionName) {
|
|
||||||
context
|
context
|
||||||
.read<EditSelectOptionBloc>()
|
.read<EditSelectOptionBloc>()
|
||||||
.add(EditSelectOptionEvent.updateName(optionName));
|
.add(EditSelectOptionEvent.updateName(newName));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -88,7 +88,8 @@ class _PopoverMaskState extends State<PopoverMask> {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _handleGlobalKeyEvent(KeyEvent event) {
|
bool _handleGlobalKeyEvent(KeyEvent event) {
|
||||||
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
if (event.logicalKey == LogicalKeyboardKey.escape &&
|
||||||
|
event is KeyDownEvent) {
|
||||||
if (widget.onExit != null) {
|
if (widget.onExit != null) {
|
||||||
widget.onExit!();
|
widget.onExit!();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||||
|
|
||||||
class FlowyTextField extends StatefulWidget {
|
class FlowyTextField extends StatefulWidget {
|
||||||
final String hintText;
|
final String hintText;
|
||||||
final String text;
|
final String text;
|
||||||
final void Function(String)? onChanged;
|
final void Function(String)? onChanged;
|
||||||
final void Function(String)? onSubmitted;
|
final void Function(String)? onSubmitted;
|
||||||
|
final void Function()? onCanceled;
|
||||||
final bool autoFucous;
|
final bool autoFucous;
|
||||||
|
final int? maxLength;
|
||||||
|
final TextEditingController? controller;
|
||||||
|
final bool autoClearWhenDone;
|
||||||
const FlowyTextField({
|
const FlowyTextField({
|
||||||
this.hintText = "",
|
this.hintText = "",
|
||||||
this.text = "",
|
this.text = "",
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.onSubmitted,
|
this.onSubmitted,
|
||||||
|
this.onCanceled,
|
||||||
this.autoFucous = true,
|
this.autoFucous = true,
|
||||||
|
this.maxLength,
|
||||||
|
this.controller,
|
||||||
|
this.autoClearWhenDone = false,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@ -23,12 +33,19 @@ class FlowyTextField extends StatefulWidget {
|
||||||
class FlowyTextFieldState extends State<FlowyTextField> {
|
class FlowyTextFieldState extends State<FlowyTextField> {
|
||||||
late FocusNode focusNode;
|
late FocusNode focusNode;
|
||||||
late TextEditingController controller;
|
late TextEditingController controller;
|
||||||
|
var textLength = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
focusNode = FocusNode();
|
focusNode = FocusNode();
|
||||||
controller = TextEditingController();
|
focusNode.addListener(notifyDidEndEditing);
|
||||||
controller.text = widget.text;
|
|
||||||
|
if (widget.controller != null) {
|
||||||
|
controller = widget.controller!;
|
||||||
|
} else {
|
||||||
|
controller = TextEditingController();
|
||||||
|
controller.text = widget.text;
|
||||||
|
}
|
||||||
if (widget.autoFucous) {
|
if (widget.autoFucous) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
focusNode.requestFocus();
|
focusNode.requestFocus();
|
||||||
|
@ -47,9 +64,15 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||||
},
|
},
|
||||||
onSubmitted: (text) {
|
onSubmitted: (text) {
|
||||||
widget.onSubmitted?.call(text);
|
widget.onSubmitted?.call(text);
|
||||||
|
|
||||||
|
if (widget.autoClearWhenDone) {
|
||||||
|
controller.text = "";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
maxLength: widget.maxLength,
|
||||||
|
maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds,
|
||||||
|
style: TextStyles.body1.size(FontSizes.s12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.all(10),
|
contentPadding: const EdgeInsets.all(10),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
|
@ -61,6 +84,8 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||||
),
|
),
|
||||||
isDense: true,
|
isDense: true,
|
||||||
hintText: widget.hintText,
|
hintText: widget.hintText,
|
||||||
|
suffixText: _suffixText(),
|
||||||
|
counterText: "",
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
@ -71,4 +96,29 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
focusNode.removeListener(notifyDidEndEditing);
|
||||||
|
focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDidEndEditing() {
|
||||||
|
if (!focusNode.hasFocus) {
|
||||||
|
if (controller.text.isEmpty) {
|
||||||
|
widget.onCanceled?.call();
|
||||||
|
} else {
|
||||||
|
widget.onSubmitted?.call(controller.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _suffixText() {
|
||||||
|
if (widget.maxLength != null) {
|
||||||
|
return '${textLength.toString()}/${widget.maxLength.toString()}';
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,10 +80,10 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for Checklist
|
||||||
}
|
}
|
||||||
|
|
||||||
new_cell_data = select_ids.to_string();
|
new_cell_data = select_ids.to_string();
|
||||||
tracing::trace!("checklist's cell data: {}", &new_cell_data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::trace!("checklist's cell data: {}", &new_cell_data);
|
||||||
Ok(new_cell_data)
|
Ok(new_cell_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl SelectOptionTypeOptionTransformer {
|
||||||
T: SelectTypeOptionSharedAction,
|
T: SelectTypeOptionSharedAction,
|
||||||
{
|
{
|
||||||
match decoded_field_type {
|
match decoded_field_type {
|
||||||
FieldType::SingleSelect | FieldType::MultiSelect => {
|
FieldType::SingleSelect | FieldType::MultiSelect | FieldType::Checklist => {
|
||||||
//
|
//
|
||||||
CellBytes::from(shared.get_selected_options(cell_data))
|
CellBytes::from(shared.get_selected_options(cell_data))
|
||||||
}
|
}
|
||||||
|
|
|
@ -390,7 +390,14 @@ fn make_test_board() -> BuildGridContext {
|
||||||
let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
|
let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
|
||||||
grid_builder.add_field(url_field);
|
grid_builder.add_field(url_field);
|
||||||
}
|
}
|
||||||
FieldType::Checklist => {}
|
FieldType::Checklist => {
|
||||||
|
let checklist = ChecklistTypeOptionBuilder::default()
|
||||||
|
.add_option(SelectOptionPB::new(FIRST_THING))
|
||||||
|
.add_option(SelectOptionPB::new(SECOND_THING))
|
||||||
|
.add_option(SelectOptionPB::new(THIRD_THING));
|
||||||
|
let checklist_field = FieldBuilder::new(checklist).name("TODO").visibility(true).build();
|
||||||
|
grid_builder.add_field(checklist_field);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue