fix: fix bugs

This commit is contained in:
appflowy 2022-03-30 22:51:52 +08:00
parent 065a72a8da
commit 547b8ec29c
11 changed files with 373 additions and 108 deletions

View File

@ -177,7 +177,9 @@
"limeColor": "Lime",
"greenColor": "Green",
"aquaColor": "Aqua",
"blueColor": "Blue"
"blueColor": "Blue",
"deleteTag": "Delete tag",
"colorPannelTitle": "Colors"
}
}
}

View File

@ -19,6 +19,12 @@ class OptionPannelBloc extends Bloc<OptionPannelEvent, OptionPannelState> {
endAddingOption: (_EndAddingOption value) {
emit(state.copyWith(isEditingOption: false, newOptionName: none()));
},
updateOption: (_UpdateOption value) {
emit(state.copyWith(updateOption: Some(value.option)));
},
deleteOption: (_DeleteOption value) {
emit(state.copyWith(deleteOption: Some(value.option)));
},
);
},
);
@ -35,6 +41,8 @@ class OptionPannelEvent with _$OptionPannelEvent {
const factory OptionPannelEvent.createOption(String optionName) = _CreateOption;
const factory OptionPannelEvent.beginAddingOption() = _BeginAddingOption;
const factory OptionPannelEvent.endAddingOption() = _EndAddingOption;
const factory OptionPannelEvent.updateOption(SelectOption option) = _UpdateOption;
const factory OptionPannelEvent.deleteOption(SelectOption option) = _DeleteOption;
}
@freezed
@ -43,11 +51,15 @@ class OptionPannelState with _$OptionPannelState {
required List<SelectOption> options,
required bool isEditingOption,
required Option<String> newOptionName,
required Option<SelectOption> updateOption,
required Option<SelectOption> deleteOption,
}) = _OptionPannelState;
factory OptionPannelState.initial(List<SelectOption> options) => OptionPannelState(
options: options,
isEditingOption: false,
newOptionName: none(),
updateOption: none(),
deleteOption: none(),
);
}

View File

@ -3,7 +3,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
import 'type_option_service.dart';
part 'single_select_bloc.freezed.dart';
@ -11,9 +11,13 @@ part 'single_select_bloc.freezed.dart';
class SingleSelectTypeOptionBloc extends Bloc<SingleSelectTypeOptionEvent, SingleSelectTypeOptionState> {
final TypeOptionService service;
SingleSelectTypeOptionBloc(SingleSelectTypeOption typeOption, String fieldId)
: service = TypeOptionService(fieldId: fieldId),
super(SingleSelectTypeOptionState.initial(typeOption)) {
SingleSelectTypeOptionBloc(
SingleSelectTypeOption typeOption,
String fieldId,
) : service = TypeOptionService(fieldId: fieldId),
super(
SingleSelectTypeOptionState.initial(typeOption),
) {
on<SingleSelectTypeOptionEvent>(
(event, emit) async {
await event.map(
@ -21,13 +25,17 @@ class SingleSelectTypeOptionBloc extends Bloc<SingleSelectTypeOptionEvent, Singl
final result = await service.createOption(value.optionName);
result.fold(
(option) {
state.typeOption.options.insert(0, option);
emit(state);
emit(state.copyWith(typeOption: _insertOption(option)));
},
(err) => Log.error(err),
);
},
updateOptions: (_UpdateOptions value) async {},
updateOption: (_UpdateOption value) async {
emit(state.copyWith(typeOption: _updateOption(value.option)));
},
deleteOption: (_DeleteOption value) {
emit(state.copyWith(typeOption: _deleteOption(value.option)));
},
);
},
);
@ -37,12 +45,40 @@ class SingleSelectTypeOptionBloc extends Bloc<SingleSelectTypeOptionEvent, Singl
Future<void> close() async {
return super.close();
}
SingleSelectTypeOption _insertOption(SelectOption option) {
state.typeOption.freeze();
return state.typeOption.rebuild((typeOption) {
typeOption.options.insert(0, option);
});
}
SingleSelectTypeOption _updateOption(SelectOption option) {
state.typeOption.freeze();
return state.typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options[index] = option;
}
});
}
SingleSelectTypeOption _deleteOption(SelectOption option) {
state.typeOption.freeze();
return state.typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options.removeAt(index);
}
});
}
}
@freezed
class SingleSelectTypeOptionEvent with _$SingleSelectTypeOptionEvent {
const factory SingleSelectTypeOptionEvent.createOption(String optionName) = _CreateOption;
const factory SingleSelectTypeOptionEvent.updateOptions(List<SelectOption> options) = _UpdateOptions;
const factory SingleSelectTypeOptionEvent.updateOption(SelectOption option) = _UpdateOption;
const factory SingleSelectTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption;
}
@freezed

View File

@ -79,7 +79,7 @@ class _FieldTypeSwitcherState extends State<FieldTypeSwitcher> {
final list = FieldTypeList(onSelectField: (fieldType) {
context.read<FieldTypeSwitchBloc>().add(FieldTypeSwitchEvent.toFieldType(fieldType));
});
_showOverlay(context, FieldTypeList.identifier(), list);
_showOverlay(context, list);
},
leftIcon: svg(field.fieldType.iconName(), color: theme.iconColor),
rightIcon: svg("grid/more", color: theme.iconColor),
@ -92,18 +92,27 @@ class _FieldTypeSwitcherState extends State<FieldTypeSwitcher> {
required Field field,
required TypeOptionData data,
}) {
final delegate = TypeOptionOperationDelegate(
didUpdateTypeOptionData: (data) {
context.read<FieldTypeSwitchBloc>().add(FieldTypeSwitchEvent.didUpdateTypeOptionData(data));
},
requireToShowOverlay: _showOverlay,
final overlayDelegate = TypeOptionOverlayDelegate(
showOverlay: _showOverlay,
hideOverlay: _hideOverlay,
);
final builder = _makeTypeOptionBuild(field: field, data: data, delegate: delegate);
final dataDelegate = TypeOptionDataDelegate(didUpdateTypeOptionData: (data) {
context.read<FieldTypeSwitchBloc>().add(FieldTypeSwitchEvent.didUpdateTypeOptionData(data));
});
final builder = _makeTypeOptionBuild(
field: field,
data: data,
overlayDelegate: overlayDelegate,
dataDelegate: dataDelegate,
);
return builder.customWidget;
}
void _showOverlay(BuildContext context, String identifier, Widget child) {
void _showOverlay(BuildContext context, Widget child, {VoidCallback? onRemoved}) {
final identifier = child.toString();
if (currentOverlayIdentifier != null) {
FlowyOverlay.of(context).remove(currentOverlayIdentifier!);
}
@ -112,7 +121,7 @@ class _FieldTypeSwitcherState extends State<FieldTypeSwitcher> {
FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer(
child: child,
constraints: BoxConstraints.loose(const Size(240, 400)),
constraints: BoxConstraints.loose(const Size(340, 400)),
),
identifier: identifier,
anchorContext: context,
@ -136,19 +145,20 @@ abstract class TypeOptionBuilder {
TypeOptionBuilder _makeTypeOptionBuild({
required Field field,
required TypeOptionData data,
required TypeOptionOperationDelegate delegate,
required TypeOptionOverlayDelegate overlayDelegate,
required TypeOptionDataDelegate dataDelegate,
}) {
switch (field.fieldType) {
case FieldType.Checkbox:
return CheckboxTypeOptionBuilder(data);
case FieldType.DateTime:
return DateTypeOptionBuilder(data, delegate);
return DateTypeOptionBuilder(data, overlayDelegate, dataDelegate);
case FieldType.SingleSelect:
return SingleSelectTypeOptionBuilder(field.id, data, delegate);
return SingleSelectTypeOptionBuilder(field.id, data, overlayDelegate, dataDelegate);
case FieldType.MultiSelect:
return MultiSelectTypeOptionBuilder(data, delegate);
return MultiSelectTypeOptionBuilder(data, overlayDelegate);
case FieldType.Number:
return NumberTypeOptionBuilder(data, delegate);
return NumberTypeOptionBuilder(data, overlayDelegate, dataDelegate);
case FieldType.RichText:
return RichTextTypeOptionBuilder(data);
@ -163,20 +173,30 @@ abstract class TypeOptionWidget extends StatelessWidget {
typedef TypeOptionData = Uint8List;
typedef TypeOptionDataCallback = void Function(TypeOptionData typeOptionData);
typedef ShowOverlayCallback = void Function(BuildContext anchorContext, String overlayIdentifier, Widget child);
typedef ShowOverlayCallback = void Function(
BuildContext anchorContext,
Widget child, {
VoidCallback? onRemoved,
});
typedef HideOverlayCallback = void Function(BuildContext anchorContext);
class TypeOptionOperationDelegate {
TypeOptionDataCallback didUpdateTypeOptionData;
ShowOverlayCallback requireToShowOverlay;
class TypeOptionOverlayDelegate {
ShowOverlayCallback showOverlay;
HideOverlayCallback hideOverlay;
TypeOptionOperationDelegate({
required this.didUpdateTypeOptionData,
required this.requireToShowOverlay,
TypeOptionOverlayDelegate({
required this.showOverlay,
required this.hideOverlay,
});
}
class TypeOptionDataDelegate {
TypeOptionDataCallback didUpdateTypeOptionData;
TypeOptionDataDelegate({
required this.didUpdateTypeOptionData,
});
}
class RichTextTypeOptionBuilder extends TypeOptionBuilder {
RichTextTypeOption typeOption;

View File

@ -17,22 +17,6 @@ class FieldTypeList extends StatelessWidget {
final SelectFieldCallback onSelectField;
const FieldTypeList({required this.onSelectField, Key? key}) : super(key: key);
static void show(BuildContext context, SelectFieldCallback onSelectField) {
final list = FieldTypeList(onSelectField: onSelectField);
FieldTypeList.hide(context);
FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer(
child: list,
constraints: BoxConstraints.loose(const Size(140, 300)),
),
identifier: FieldTypeList.identifier(),
anchorContext: context,
anchorDirection: AnchorDirection.leftWithCenterAligned,
style: FlowyOverlayStyle(blur: false),
anchorOffset: const Offset(-20, 0),
);
}
static void hide(BuildContext context) {
FlowyOverlay.of(context).remove(FieldTypeList.identifier());
}

View File

@ -15,30 +15,39 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DateTypeOptionBuilder extends TypeOptionBuilder {
DateTypeOption typeOption;
TypeOptionOperationDelegate delegate;
final DateTypeOptionWidget _widget;
DateTypeOptionBuilder(TypeOptionData typeOptionData, this.delegate)
: typeOption = DateTypeOption.fromBuffer(typeOptionData);
DateTypeOptionBuilder(
TypeOptionData typeOptionData,
TypeOptionOverlayDelegate overlayDelegate,
TypeOptionDataDelegate dataDelegate,
) : _widget = DateTypeOptionWidget(
typeOption: DateTypeOption.fromBuffer(typeOptionData),
dataDelegate: dataDelegate,
overlayDelegate: overlayDelegate,
);
@override
Widget? get customWidget => DateTypeOptionWidget(
typeOption: typeOption,
operationDelegate: delegate,
);
Widget? get customWidget => _widget;
}
class DateTypeOptionWidget extends TypeOptionWidget {
final DateTypeOption typeOption;
final TypeOptionOperationDelegate operationDelegate;
const DateTypeOptionWidget({required this.typeOption, required this.operationDelegate, Key? key}) : super(key: key);
final TypeOptionOverlayDelegate overlayDelegate;
final TypeOptionDataDelegate dataDelegate;
const DateTypeOptionWidget({
required this.typeOption,
required this.dataDelegate,
required this.overlayDelegate,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<DateTypeOptionBloc>(param1: typeOption),
child: BlocConsumer<DateTypeOptionBloc, DateTypeOptionState>(
listener: (context, state) => operationDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()),
listener: (context, state) => dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()),
builder: (context, state) {
return Column(children: [
_dateFormatButton(context),
@ -61,7 +70,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
final list = DateFormatList(onSelected: (format) {
context.read<DateTypeOptionBloc>().add(DateTypeOptionEvent.didSelectDateFormat(format));
});
operationDelegate.requireToShowOverlay(context, list.identifier(), list);
overlayDelegate.showOverlay(context, list);
},
rightIcon: svg("grid/more", color: theme.iconColor),
),
@ -80,7 +89,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
final list = TimeFormatList(onSelected: (format) {
context.read<DateTypeOptionBloc>().add(DateTypeOptionEvent.didSelectTimeFormat(format));
});
operationDelegate.requireToShowOverlay(context, list.identifier(), list);
overlayDelegate.showOverlay(context, list);
},
rightIcon: svg("grid/more", color: theme.iconColor),
),

View File

@ -1,18 +1,151 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/edit_option_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/widget.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.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/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
class EditSelectOptionPannel extends StatelessWidget {
final SelectOption option;
final VoidCallback onDeleted;
final Function(SelectOption) onUpdated;
const EditSelectOptionPannel({
required this.option,
required this.onDeleted,
required this.onUpdated,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => EditOptionBloc(option: option),
child: MultiBlocListener(
listeners: [
BlocListener<EditOptionBloc, EditOptionState>(
listenWhen: (p, c) => p.deleted != c.deleted,
listener: (context, state) {
state.deleted.fold(() => null, (_) => onDeleted());
},
),
BlocListener<EditOptionBloc, EditOptionState>(
listenWhen: (p, c) => p.option != c.option,
listener: (context, state) {
onUpdated(state.option);
},
),
],
child: BlocBuilder<EditOptionBloc, EditOptionState>(
builder: (context, state) {
List<Widget> slivers = [
SliverToBoxAdapter(child: _OptionNameTextField(state.option.name)),
const SliverToBoxAdapter(child: VSpace(10)),
const SliverToBoxAdapter(child: _DeleteTag()),
const SliverToBoxAdapter(child: TypeOptionSeparator()),
const SliverToBoxAdapter(child: SelectOptionColorList()),
];
return CustomScrollView(
slivers: slivers,
controller: ScrollController(),
physics: StyledScrollPhysics(),
);
},
),
),
);
}
}
class _DeleteTag extends StatelessWidget {
const _DeleteTag({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return SizedBox(
height: GridSize.typeOptionItemHeight,
child: FlowyButton(
text: FlowyText.medium(LocaleKeys.grid_selectOption_deleteTag.tr(), fontSize: 12),
hoverColor: theme.hover,
leftIcon: svg("grid/delete", color: theme.iconColor),
onTap: () {
context.read<EditOptionBloc>().add(const EditOptionEvent.delete());
},
),
);
}
}
class _OptionNameTextField extends StatelessWidget {
final String name;
const _OptionNameTextField(this.name, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return NameTextField(
name: name,
onCanceled: () {},
onDone: (optionName) {
context.read<EditOptionBloc>().add(EditOptionEvent.updateName(optionName));
},
);
}
}
class SelectOptionColorList extends StatelessWidget {
const SelectOptionColorList({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container();
final optionItems = SelectOptionColor.values.map((option) {
// Color color = option.color();
// var hex = option.color.value.toRadixString(16);
// if (hex.startsWith('ff')) {
// hex = hex.substring(2);
// }
// hex = '#$hex';
return _SelectOptionColorItem(option: option, isSelected: true);
}).toList();
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: GridSize.typeOptionContentInsets,
child: SizedBox(
height: GridSize.typeOptionItemHeight,
child: FlowyText.medium(
LocaleKeys.grid_selectOption_colorPannelTitle.tr(),
fontSize: 12,
textAlign: TextAlign.left,
),
),
),
ListView.separated(
shrinkWrap: true,
controller: ScrollController(),
separatorBuilder: (context, index) {
return VSpace(GridSize.typeOptionSeparatorHeight);
},
itemCount: optionItems.length,
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return optionItems[index];
},
),
],
);
}
}
@ -24,12 +157,12 @@ class _SelectOptionColorItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
Widget? checkmark;
if (isSelected) {
checkmark = svg("grid/details", color: theme.iconColor);
}
final String hex = '#${option.color(context).value.toRadixString(16)}';
final colorIcon = SizedBox.square(
dimension: 16,
child: Container(
@ -40,15 +173,17 @@ class _SelectOptionColorItem extends StatelessWidget {
),
);
return FlowyButton(
text: FlowyText.medium(
option.name(),
fontSize: 12,
return SizedBox(
height: GridSize.typeOptionItemHeight,
child: FlowyButton(
text: FlowyText.medium(option.name(), fontSize: 12),
hoverColor: theme.hover,
leftIcon: colorIcon,
rightIcon: checkmark,
onTap: () {
context.read<EditOptionBloc>().add(EditOptionEvent.updateColor(hex));
},
),
hoverColor: theme.hover,
leftIcon: colorIcon,
rightIcon: checkmark,
onTap: () {},
);
}
}

View File

@ -9,7 +9,7 @@ import 'option_pannel.dart';
class MultiSelectTypeOptionBuilder extends TypeOptionBuilder {
MultiSelectTypeOption typeOption;
TypeOptionOperationDelegate delegate;
TypeOptionOverlayDelegate delegate;
MultiSelectTypeOptionBuilder(TypeOptionData typeOptionData, this.delegate)
: typeOption = MultiSelectTypeOption.fromBuffer(typeOptionData);
@ -20,8 +20,8 @@ class MultiSelectTypeOptionBuilder extends TypeOptionBuilder {
class MultiSelectTypeOptionWidget extends TypeOptionWidget {
final MultiSelectTypeOption typeOption;
final TypeOptionOperationDelegate delegate;
const MultiSelectTypeOptionWidget(this.typeOption, this.delegate, {Key? key}) : super(key: key);
final TypeOptionOverlayDelegate overlayDelegate;
const MultiSelectTypeOptionWidget(this.typeOption, this.overlayDelegate, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -32,14 +32,14 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget {
return OptionPannel(
options: state.typeOption.options,
beginEdit: () {
delegate.hideOverlay(context);
overlayDelegate.hideOverlay(context);
},
createOptionCallback: (name) {
context.read<MultiSelectTypeOptionBloc>().add(MultiSelectTypeOptionEvent.createOption(name));
},
updateOptionsCallback: (options) {
context.read<MultiSelectTypeOptionBloc>().add(MultiSelectTypeOptionEvent.updateOptions(options));
},
updateOptionCallback: (updateOption) {},
deleteOptionCallback: (deleteOption) {},
overlayDelegate: overlayDelegate,
);
},
),

View File

@ -15,25 +15,29 @@ import 'package:easy_localization/easy_localization.dart' hide NumberFormat;
import 'package:app_flowy/generated/locale_keys.g.dart';
class NumberTypeOptionBuilder extends TypeOptionBuilder {
NumberTypeOption typeOption;
TypeOptionOperationDelegate delegate;
final NumberTypeOptionWidget _widget;
NumberTypeOptionBuilder(
TypeOptionData typeOptionData,
this.delegate,
) : typeOption = NumberTypeOption.fromBuffer(typeOptionData);
TypeOptionOverlayDelegate overlayDelegate,
TypeOptionDataDelegate dataDelegate,
) : _widget = NumberTypeOptionWidget(
typeOption: NumberTypeOption.fromBuffer(typeOptionData),
dataDelegate: dataDelegate,
overlayDelegate: overlayDelegate,
);
@override
Widget? get customWidget => NumberTypeOptionWidget(
typeOption: typeOption,
operationDelegate: delegate,
);
Widget? get customWidget => _widget;
}
class NumberTypeOptionWidget extends TypeOptionWidget {
final TypeOptionOperationDelegate operationDelegate;
final TypeOptionDataDelegate dataDelegate;
final TypeOptionOverlayDelegate overlayDelegate;
final NumberTypeOption typeOption;
const NumberTypeOptionWidget({required this.typeOption, required this.operationDelegate, Key? key}) : super(key: key);
const NumberTypeOptionWidget(
{required this.typeOption, required this.dataDelegate, required this.overlayDelegate, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -43,7 +47,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
child: SizedBox(
height: GridSize.typeOptionItemHeight,
child: BlocConsumer<NumberTypeOptionBloc, NumberTypeOptionState>(
listener: (context, state) => operationDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()),
listener: (context, state) => dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()),
builder: (context, state) {
return FlowyButton(
text: FlowyText.medium(LocaleKeys.grid_field_numberFormat.tr(), fontSize: 12),
@ -53,7 +57,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
final list = NumberFormatList(onSelected: (format) {
context.read<NumberTypeOptionBloc>().add(NumberTypeOptionEvent.didSelectFormat(format));
});
operationDelegate.requireToShowOverlay(context, list.identifier(), list);
overlayDelegate.showOverlay(context, list);
},
rightIcon: svg("grid/more", color: theme.iconColor),
);

View File

@ -1,5 +1,6 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/option_pannel_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
@ -11,18 +12,24 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'edit_option_pannel.dart';
import 'widget.dart';
class OptionPannel extends StatelessWidget {
final List<SelectOption> options;
final VoidCallback beginEdit;
final Function(String optionName) createOptionCallback;
final Function(List<SelectOption>) updateOptionsCallback;
final Function(SelectOption) updateOptionCallback;
final Function(SelectOption) deleteOptionCallback;
final TypeOptionOverlayDelegate overlayDelegate;
const OptionPannel({
required this.options,
required this.beginEdit,
required this.createOptionCallback,
required this.updateOptionsCallback,
required this.updateOptionCallback,
required this.deleteOptionCallback,
required this.overlayDelegate,
Key? key,
}) : super(key: key);
@ -39,6 +46,16 @@ class OptionPannel extends StatelessWidget {
() => null,
(optionName) => createOptionCallback(optionName),
);
state.updateOption.fold(
() => null,
(updateOption) => updateOptionCallback(updateOption),
);
state.deleteOption.fold(
() => null,
(deleteOption) => deleteOptionCallback(deleteOption),
);
},
builder: (context, state) {
List<Widget> children = [
@ -46,7 +63,7 @@ class OptionPannel extends StatelessWidget {
const OptionTitle(),
];
if (state.isEditingOption) {
children.add(const _AddOptionTextField());
children.add(const _OptionNameTextField());
}
if (state.options.isEmpty && !state.isEditingOption) {
@ -54,7 +71,7 @@ class OptionPannel extends StatelessWidget {
}
if (state.options.isNotEmpty) {
children.add(_OptionList(key: ObjectKey(state.options)));
children.add(_OptionList(overlayDelegate));
}
return Column(children: children);
@ -105,14 +122,18 @@ class OptionTitle extends StatelessWidget {
}
class _OptionList extends StatelessWidget {
const _OptionList({Key? key}) : super(key: key);
final TypeOptionOverlayDelegate delegate;
const _OptionList(this.delegate, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<OptionPannelBloc, OptionPannelState>(
buildWhen: (previous, current) {
return previous.options != current.options;
},
builder: (context, state) {
final optionItems = state.options.map((option) {
return _OptionItem(option: option);
return _makeOptionItem(context, option);
}).toList();
return ListView.separated(
@ -129,11 +150,37 @@ class _OptionList extends StatelessWidget {
},
);
}
_OptionItem _makeOptionItem(BuildContext context, SelectOption option) {
return _OptionItem(
option: option,
onEdited: (option) {
final pannel = EditSelectOptionPannel(
option: option,
onDeleted: () {
delegate.hideOverlay(context);
context.read<OptionPannelBloc>().add(OptionPannelEvent.deleteOption(option));
},
onUpdated: (updatedOption) {
delegate.hideOverlay(context);
context.read<OptionPannelBloc>().add(OptionPannelEvent.updateOption(updatedOption));
},
key: ValueKey(option.id),
);
delegate.showOverlay(context, pannel);
},
);
}
}
class _OptionItem extends StatelessWidget {
final SelectOption option;
const _OptionItem({required this.option, Key? key}) : super(key: key);
final Function(SelectOption) onEdited;
const _OptionItem({
required this.option,
required this.onEdited,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -143,7 +190,7 @@ class _OptionItem extends StatelessWidget {
child: FlowyButton(
text: FlowyText.medium(option.name, fontSize: 12),
hoverColor: theme.hover,
onTap: () {},
onTap: () => onEdited(option),
rightIcon: svg("grid/details", color: theme.iconColor),
),
);
@ -170,8 +217,8 @@ class _AddOptionButton extends StatelessWidget {
}
}
class _AddOptionTextField extends StatelessWidget {
const _AddOptionTextField({Key? key}) : super(key: key);
class _OptionNameTextField extends StatelessWidget {
const _OptionNameTextField({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -12,11 +12,13 @@ class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
SingleSelectTypeOptionBuilder(
String fieldId,
TypeOptionData typeOptionData,
TypeOptionOperationDelegate delegate,
TypeOptionOverlayDelegate overlayDelegate,
TypeOptionDataDelegate dataDelegate,
) : _widget = SingleSelectTypeOptionWidget(
fieldId,
SingleSelectTypeOption.fromBuffer(typeOptionData),
delegate,
fieldId: fieldId,
typeOption: SingleSelectTypeOption.fromBuffer(typeOptionData),
dataDelegate: dataDelegate,
overlayDelegate: overlayDelegate,
);
@override
@ -26,27 +28,41 @@ class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
class SingleSelectTypeOptionWidget extends TypeOptionWidget {
final String fieldId;
final SingleSelectTypeOption typeOption;
final TypeOptionOperationDelegate delegate;
const SingleSelectTypeOptionWidget(this.fieldId, this.typeOption, this.delegate, {Key? key}) : super(key: key);
final TypeOptionOverlayDelegate overlayDelegate;
final TypeOptionDataDelegate dataDelegate;
const SingleSelectTypeOptionWidget({
required this.fieldId,
required this.typeOption,
required this.dataDelegate,
required this.overlayDelegate,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<SingleSelectTypeOptionBloc>(param1: typeOption, param2: fieldId),
child: BlocConsumer<SingleSelectTypeOptionBloc, SingleSelectTypeOptionState>(
listener: (context, state) => delegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()),
listener: (context, state) {
dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer());
},
builder: (context, state) {
return OptionPannel(
options: state.typeOption.options,
beginEdit: () {
delegate.hideOverlay(context);
overlayDelegate.hideOverlay(context);
},
createOptionCallback: (name) {
context.read<SingleSelectTypeOptionBloc>().add(SingleSelectTypeOptionEvent.createOption(name));
},
updateOptionsCallback: (options) {
context.read<SingleSelectTypeOptionBloc>().add(SingleSelectTypeOptionEvent.updateOptions(options));
updateOptionCallback: (updateOption) {
context.read<SingleSelectTypeOptionBloc>().add(SingleSelectTypeOptionEvent.updateOption(updateOption));
},
deleteOptionCallback: (deleteOption) {
context.read<SingleSelectTypeOptionBloc>().add(SingleSelectTypeOptionEvent.deleteOption(deleteOption));
},
overlayDelegate: overlayDelegate,
key: ValueKey(state.typeOption.hashCode),
);
},
),