Merge pull request #1102 from AppFlowy-IO/fix/ui_bugs

Fix/UI bugs
This commit is contained in:
Nathan.fooo 2022-09-20 17:50:45 +08:00 committed by GitHub
commit bb61bf0a30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 260 additions and 234 deletions

View File

@ -33,8 +33,10 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
await dataController.loadTypeOptionData(); await dataController.loadTypeOptionData();
}, },
updateName: (name) { updateName: (name) {
dataController.fieldName = name; if (state.name != name) {
emit(state.copyWith(name: name)); dataController.fieldName = name;
emit(state.copyWith(name: name));
}
}, },
didReceiveFieldChanged: (FieldPB field) { didReceiveFieldChanged: (FieldPB field) {
emit(state.copyWith( emit(state.copyWith(

View File

@ -58,7 +58,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final alignment = widget.cellStyle != null final alignment = widget.cellStyle != null
? widget.cellStyle!.alignment ? widget.cellStyle!.alignment
: Alignment.center; : Alignment.centerLeft;
return BlocProvider.value( return BlocProvider.value(
value: _cellBloc, value: _cellBloc,
child: BlocBuilder<DateCellBloc, DateCellState>( child: BlocBuilder<DateCellBloc, DateCellState>(
@ -77,7 +77,10 @@ class _DateCellState extends GridCellState<GridDateCell> {
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
child: Align( child: Align(
alignment: alignment, alignment: alignment,
child: FlowyText.medium(state.dateStr, fontSize: 12), child: FlowyText.medium(
state.dateStr,
fontSize: 12,
),
), ),
), ),
), ),

View File

@ -11,20 +11,17 @@ import 'package:textfield_tags/textfield_tags.dart';
import 'extension.dart'; import 'extension.dart';
class SelectOptionTextField extends StatelessWidget { class SelectOptionTextField extends StatefulWidget {
final FocusNode _focusNode;
final TextEditingController _controller;
final TextfieldTagsController tagController; final TextfieldTagsController tagController;
final List<SelectOptionPB> options; final List<SelectOptionPB> options;
final LinkedHashMap<String, SelectOptionPB> selectedOptionMap; final LinkedHashMap<String, SelectOptionPB> selectedOptionMap;
final double distanceToText; final double distanceToText;
final Function(String) onNewTag; final Function(String) onNewTag;
final Function(String) newText; final Function(String) newText;
final VoidCallback? onClick; final VoidCallback? onClick;
SelectOptionTextField({ const SelectOptionTextField({
required this.options, required this.options,
required this.selectedOptionMap, required this.selectedOptionMap,
required this.distanceToText, required this.distanceToText,
@ -35,33 +32,55 @@ class SelectOptionTextField extends StatelessWidget {
TextEditingController? textController, TextEditingController? textController,
FocusNode? focusNode, FocusNode? focusNode,
Key? key, Key? key,
}) : _controller = textController ?? TextEditingController(), }) : super(key: key);
_focusNode = focusNode ?? FocusNode(),
super(key: key); @override
State<SelectOptionTextField> createState() => _SelectOptionTextFieldState();
}
class _SelectOptionTextFieldState extends State<SelectOptionTextField> {
late FocusNode focusNode;
late TextEditingController controller;
@override
void initState() {
focusNode = FocusNode();
controller = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((_) {
focusNode.requestFocus();
});
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.watch<AppTheme>(); final theme = context.watch<AppTheme>();
return TextFieldTags( return TextFieldTags(
textEditingController: _controller, textEditingController: controller,
textfieldTagsController: tagController, textfieldTagsController: widget.tagController,
initialTags: selectedOptionMap.keys.toList(), initialTags: widget.selectedOptionMap.keys.toList(),
focusNode: _focusNode, focusNode: focusNode,
textSeparators: const [','], textSeparators: const [','],
inputfieldBuilder: (BuildContext context, editController, focusNode, inputfieldBuilder: (
error, onChanged, onSubmitted) { BuildContext context,
editController,
focusNode,
error,
onChanged,
onSubmitted,
) {
return ((context, sc, tags, onTagDelegate) { return ((context, sc, tags, onTagDelegate) {
return TextField( return TextField(
autofocus: true,
controller: editController, controller: editController,
focusNode: focusNode, focusNode: focusNode,
onTap: onClick, onTap: widget.onClick,
onChanged: (text) { onChanged: (text) {
if (onChanged != null) { if (onChanged != null) {
onChanged(text); onChanged(text);
} }
newText(text); widget.newText(text);
}, },
onSubmitted: (text) { onSubmitted: (text) {
if (onSubmitted != null) { if (onSubmitted != null) {
@ -69,7 +88,7 @@ class SelectOptionTextField extends StatelessWidget {
} }
if (text.isNotEmpty) { if (text.isNotEmpty) {
onNewTag(text); widget.onNewTag(text);
focusNode.requestFocus(); focusNode.requestFocus();
} }
}, },
@ -83,7 +102,8 @@ class SelectOptionTextField extends StatelessWidget {
isDense: true, isDense: true,
prefixIcon: _renderTags(context, sc), prefixIcon: _renderTags(context, sc),
hintText: LocaleKeys.grid_selectOption_searchOption.tr(), hintText: LocaleKeys.grid_selectOption_searchOption.tr(),
prefixIconConstraints: BoxConstraints(maxWidth: distanceToText), prefixIconConstraints:
BoxConstraints(maxWidth: widget.distanceToText),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: theme.main1, width: 1.0), borderSide: BorderSide(color: theme.main1, width: 1.0),
borderRadius: Corners.s10Border, borderRadius: Corners.s10Border,
@ -96,11 +116,11 @@ class SelectOptionTextField extends StatelessWidget {
} }
Widget? _renderTags(BuildContext context, ScrollController sc) { Widget? _renderTags(BuildContext context, ScrollController sc) {
if (selectedOptionMap.isEmpty) { if (widget.selectedOptionMap.isEmpty) {
return null; return null;
} }
final children = selectedOptionMap.values final children = widget.selectedOptionMap.values
.map((option) => .map((option) =>
SelectOptionTag.fromOption(context: context, option: option)) SelectOptionTag.fromOption(context: context, option: option))
.toList(); .toList();

View File

@ -137,9 +137,11 @@ class _DragToExpandLine extends StatelessWidget {
class FieldCellButton extends StatelessWidget { class FieldCellButton extends StatelessWidget {
final VoidCallback onTap; final VoidCallback onTap;
final FieldPB field; final FieldPB field;
final int? maxLines;
const FieldCellButton({ const FieldCellButton({
required this.field, required this.field,
required this.onTap, required this.onTap,
this.maxLines = 1,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -150,7 +152,11 @@ class FieldCellButton extends StatelessWidget {
hoverColor: theme.shader6, hoverColor: theme.shader6,
onTap: onTap, onTap: onTap,
leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor), leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
text: FlowyText.medium(field.name, fontSize: 12), text: FlowyText.medium(
field.name,
fontSize: 12,
maxLines: maxLines,
),
margin: GridSize.cellContentInsets, margin: GridSize.cellContentInsets,
); );
} }

View File

@ -1,10 +1,9 @@
import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart'; import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart';
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart'; import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:dartz/dartz.dart' show none;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:flowy_infra/theme.dart'; 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/button.dart';
import 'package:flowy_infra_ui/style_widget/text.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/rounded_input_field.dart';
@ -59,38 +58,38 @@ class _FieldEditorState extends State<FieldEditor> {
isGroupField: widget.isGroupField, isGroupField: widget.isGroupField,
loader: widget.typeOptionLoader, loader: widget.typeOptionLoader,
)..add(const FieldEditorEvent.initial()), )..add(const FieldEditorEvent.initial()),
child: BlocBuilder<FieldEditorBloc, FieldEditorState>( child: ListView(
builder: (context, state) { shrinkWrap: true,
return ListView( children: [
shrinkWrap: true, FlowyText.medium(
children: [ LocaleKeys.grid_field_editProperty.tr(),
FlowyText.medium( fontSize: 12,
LocaleKeys.grid_field_editProperty.tr(), ),
fontSize: 12, const VSpace(10),
), _FieldNameTextField(popoverMutex: popoverMutex),
const VSpace(10), const VSpace(10),
_FieldNameTextField(popoverMutex: popoverMutex), ..._addDeleteFieldButton(),
const VSpace(10), _FieldTypeOptionCell(popoverMutex: popoverMutex),
..._addDeleteFieldButton(state), ],
_FieldTypeOptionCell(popoverMutex: popoverMutex),
],
);
},
), ),
); );
} }
List<Widget> _addDeleteFieldButton(FieldEditorState state) { List<Widget> _addDeleteFieldButton() {
if (widget.onDeleted == null) { if (widget.onDeleted == null) {
return []; return [];
} }
return [ return [
_DeleteFieldButton( BlocBuilder<FieldEditorBloc, FieldEditorState>(
popoverMutex: popoverMutex, builder: (context, state) {
onDeleted: () { return _DeleteFieldButton(
state.field.fold( popoverMutex: popoverMutex,
() => Log.error('Can not delete the field'), onDeleted: () {
(field) => widget.onDeleted?.call(field.id), state.field.fold(
() => Log.error('Can not delete the field'),
(field) => widget.onDeleted?.call(field.id),
);
},
); );
}, },
), ),
@ -139,13 +138,13 @@ class _FieldNameTextField extends StatefulWidget {
} }
class _FieldNameTextFieldState extends State<_FieldNameTextField> { class _FieldNameTextFieldState extends State<_FieldNameTextField> {
late String name;
FocusNode focusNode = FocusNode(); FocusNode focusNode = FocusNode();
VoidCallback? _popoverCallback; VoidCallback? _popoverCallback;
TextEditingController controller = TextEditingController(); late TextEditingController controller;
@override @override
void initState() { void initState() {
controller = TextEditingController();
focusNode.addListener(() { focusNode.addListener(() {
if (focusNode.hasFocus) { if (focusNode.hasFocus) {
widget.popoverMutex.close(); widget.popoverMutex.close();
@ -158,20 +157,29 @@ class _FieldNameTextFieldState extends State<_FieldNameTextField> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.watch<AppTheme>(); final theme = context.watch<AppTheme>();
return MultiBlocListener(
controller.text = context.read<FieldEditorBloc>().state.name; listeners: [
return BlocListener<FieldEditorBloc, FieldEditorState>( BlocListener<FieldEditorBloc, FieldEditorState>(
listenWhen: (previous, current) => previous.name != current.name, listenWhen: (p, c) => p.field == none(),
listener: (context, state) { listener: (context, state) {
controller.text = state.name; focusNode.requestFocus();
}, },
),
BlocListener<FieldEditorBloc, FieldEditorState>(
listenWhen: (p, c) => controller.text != c.name,
listener: (context, state) {
controller.text = state.name;
},
),
],
child: BlocBuilder<FieldEditorBloc, FieldEditorState>( child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
buildWhen: (previous, current) =>
previous.errorText != current.errorText,
builder: (context, state) { builder: (context, state) {
listenOnPopoverChanged(context); listenOnPopoverChanged(context);
return RoundedInputField( return RoundedInputField(
height: 36, height: 36,
autoFocus: true,
focusNode: focusNode, focusNode: focusNode,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
controller: controller, controller: controller,
@ -193,23 +201,15 @@ class _FieldNameTextFieldState extends State<_FieldNameTextField> {
void listenOnPopoverChanged(BuildContext context) { void listenOnPopoverChanged(BuildContext context) {
if (_popoverCallback != null) { if (_popoverCallback != null) {
widget.popoverMutex.removePopoverStateListener(_popoverCallback!); widget.popoverMutex.removePopoverListener(_popoverCallback!);
} }
_popoverCallback = widget.popoverMutex.listenOnPopoverStateChanged(() { _popoverCallback = widget.popoverMutex.listenOnPopoverChanged(() {
if (focusNode.hasFocus) { if (focusNode.hasFocus) {
final node = FocusScope.of(context); final node = FocusScope.of(context);
node.unfocus(); node.unfocus();
} }
}); });
} }
@override
void didUpdateWidget(covariant _FieldNameTextField oldWidget) {
controller.selection = TextSelection.fromPosition(
TextPosition(offset: controller.text.length));
super.didUpdateWidget(oldWidget);
}
} }
class _DeleteFieldButton extends StatelessWidget { class _DeleteFieldButton extends StatelessWidget {
@ -235,29 +235,11 @@ class _DeleteFieldButton extends StatelessWidget {
fontSize: 12, fontSize: 12,
color: enable ? null : theme.shader4, color: enable ? null : theme.shader4,
), ),
onTap: () => onDeleted?.call(),
); );
if (enable) button = _wrapPopover(button); // if (enable) button = button;
return button; return button;
}, },
); );
} }
Widget _wrapPopover(Widget widget) {
return AppFlowyPopover(
triggerActions: PopoverTriggerFlags.click,
constraints: BoxConstraints.loose(const Size(400, 240)),
mutex: popoverMutex,
direction: PopoverDirection.center,
popupBuilder: (popupContext) {
return PopoverAlertView(
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
cancel: () {},
confirm: () {
onDeleted?.call();
},
);
},
child: widget,
);
}
} }

View File

@ -65,7 +65,7 @@ class FieldTypeOptionEditor extends StatelessWidget {
return SizedBox( return SizedBox(
height: GridSize.typeOptionItemHeight, height: GridSize.typeOptionItemHeight,
child: AppFlowyPopover( child: AppFlowyPopover(
constraints: BoxConstraints.loose(const Size(460, 440)), constraints: BoxConstraints.loose(const Size(460, 540)),
asBarrier: true, asBarrier: true,
triggerActions: PopoverTriggerFlags.click | PopoverTriggerFlags.hover, triggerActions: PopoverTriggerFlags.click | PopoverTriggerFlags.hover,
mutex: popoverMutex, mutex: popoverMutex,

View File

@ -64,6 +64,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
Widget _renderDateFormatButton(BuildContext context, DateFormat dataFormat) { Widget _renderDateFormatButton(BuildContext context, DateFormat dataFormat) {
return AppFlowyPopover( return AppFlowyPopover(
mutex: popoverMutex, mutex: popoverMutex,
asBarrier: true,
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click, triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
offset: const Offset(20, 0), offset: const Offset(20, 0),
constraints: BoxConstraints.loose(const Size(460, 440)), constraints: BoxConstraints.loose(const Size(460, 440)),
@ -85,6 +86,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
Widget _renderTimeFormatButton(BuildContext context, TimeFormat timeFormat) { Widget _renderTimeFormatButton(BuildContext context, TimeFormat timeFormat) {
return AppFlowyPopover( return AppFlowyPopover(
mutex: popoverMutex, mutex: popoverMutex,
asBarrier: true,
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click, triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
offset: const Offset(20, 0), offset: const Offset(20, 0),
constraints: BoxConstraints.loose(const Size(460, 440)), constraints: BoxConstraints.loose(const Size(460, 440)),

View File

@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart'; import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart'; import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:app_flowy/plugins/grid/application/row/row_detail_bloc.dart'; import 'package:app_flowy/plugins/grid/application/row/row_detail_bloc.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -112,67 +113,61 @@ class _PropertyList extends StatelessWidget {
builder: (context, state) { builder: (context, state) {
return Column( return Column(
children: [ children: [
Expanded( Expanded(child: _wrapScrollbar(buildList(state))),
child: ScrollbarListStack(
axis: Axis.vertical,
controller: _scrollController,
barSize: GridSize.scrollBarSize,
autoHideScrollbar: false,
child: ListView.separated(
controller: _scrollController,
itemCount: state.gridCells.length,
itemBuilder: (BuildContext context, int index) {
return _RowDetailCell(
cellId: state.gridCells[index],
cellBuilder: cellBuilder,
);
},
separatorBuilder: (BuildContext context, int index) {
return const VSpace(2);
},
),
),
),
const VSpace(10), const VSpace(10),
_CreateFieldButton( _CreateFieldButton(
viewId: viewId, viewId: viewId,
onClosed: () { onClosed: _handleDidCreateField,
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 250),
curve: Curves.ease,
);
});
},
onOpened: (controller) {
return FieldEditor(
gridId: viewId,
typeOptionLoader: NewFieldTypeOptionLoader(gridId: viewId),
onDeleted: (fieldId) {
controller.close();
context
.read<RowDetailBloc>()
.add(RowDetailEvent.deleteField(fieldId));
},
);
},
), ),
], ],
); );
}, },
); );
} }
Widget buildList(RowDetailState state) {
return ListView.separated(
controller: _scrollController,
itemCount: state.gridCells.length,
itemBuilder: (BuildContext context, int index) {
return _RowDetailCell(
cellId: state.gridCells[index],
cellBuilder: cellBuilder,
);
},
separatorBuilder: (BuildContext context, int index) {
return const VSpace(2);
},
);
}
Widget _wrapScrollbar(Widget child) {
return ScrollbarListStack(
axis: Axis.vertical,
controller: _scrollController,
barSize: GridSize.scrollBarSize,
autoHideScrollbar: false,
child: child,
);
}
void _handleDidCreateField() {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 250),
curve: Curves.ease,
);
});
}
} }
class _CreateFieldButton extends StatefulWidget { class _CreateFieldButton extends StatefulWidget {
final String viewId; final String viewId;
final Widget Function(PopoverController) onOpened;
final VoidCallback onClosed; final VoidCallback onClosed;
const _CreateFieldButton({ const _CreateFieldButton({
required this.viewId, required this.viewId,
required this.onOpened,
required this.onClosed, required this.onClosed,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -213,8 +208,24 @@ class _CreateFieldButtonState extends State<_CreateFieldButton> {
leftIcon: svgWidget("home/add"), leftIcon: svgWidget("home/add"),
), ),
), ),
popupBuilder: (BuildContext context) => popupBuilder: (BuildContext popOverContext) {
widget.onOpened(popoverController), return FieldEditor(
gridId: widget.viewId,
typeOptionLoader: NewFieldTypeOptionLoader(gridId: widget.viewId),
onDeleted: (fieldId) {
popoverController.close();
NavigatorAlertDialog(
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
confirm: () {
context
.read<RowDetailBloc>()
.add(RowDetailEvent.deleteField(fieldId));
},
).show(context);
},
);
},
); );
} }
@ -260,41 +271,25 @@ class _RowDetailCellState extends State<_RowDetailCell> {
), ),
); );
return ConstrainedBox( return IntrinsicHeight(
constraints: const BoxConstraints(minHeight: 40), child: ConstrainedBox(
child: IntrinsicHeight( constraints: const BoxConstraints(minHeight: 40),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
SizedBox( AppFlowyPopover(
width: 150, controller: popover,
child: Popover( constraints: BoxConstraints.loose(const Size(240, 600)),
controller: popover, popupBuilder: (popoverContext) => buildFieldEditor(),
offset: const Offset(20, 0), child: SizedBox(
popupBuilder: (popoverContext) { width: 150,
return OverlayContainer(
constraints: BoxConstraints.loose(const Size(240, 600)),
child: FieldEditor(
gridId: widget.cellId.gridId,
fieldName: widget.cellId.fieldContext.field.name,
isGroupField: widget.cellId.fieldContext.isGroupField,
typeOptionLoader: FieldTypeOptionLoader(
gridId: widget.cellId.gridId,
field: widget.cellId.fieldContext.field,
),
onDeleted: (fieldId) {
popover.close();
context
.read<RowDetailBloc>()
.add(RowDetailEvent.deleteField(fieldId));
},
),
);
},
child: FieldCellButton( child: FieldCellButton(
maxLines: null,
field: widget.cellId.fieldContext.field, field: widget.cellId.fieldContext.field,
onTap: () => popover.show(), onTap: () {
popover.show();
},
), ),
), ),
), ),
@ -305,6 +300,30 @@ class _RowDetailCellState extends State<_RowDetailCell> {
), ),
); );
} }
Widget buildFieldEditor() {
return FieldEditor(
gridId: widget.cellId.gridId,
fieldName: widget.cellId.fieldContext.field.name,
isGroupField: widget.cellId.fieldContext.isGroupField,
typeOptionLoader: FieldTypeOptionLoader(
gridId: widget.cellId.gridId,
field: widget.cellId.fieldContext.field,
),
onDeleted: (fieldId) {
popover.close();
NavigatorAlertDialog(
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
confirm: () {
context
.read<RowDetailBloc>()
.add(RowDetailEvent.deleteField(fieldId));
},
).show(context);
},
);
}
} }
GridCellStyle? _customCellStyle(AppTheme theme, FieldType fieldType) { GridCellStyle? _customCellStyle(AppTheme theme, FieldType fieldType) {

View File

@ -120,7 +120,7 @@ class _GridPropertyCell extends StatelessWidget {
mutex: popoverMutex, mutex: popoverMutex,
triggerActions: PopoverTriggerFlags.click, triggerActions: PopoverTriggerFlags.click,
offset: const Offset(20, 0), offset: const Offset(20, 0),
constraints: BoxConstraints.loose(const Size(240, 200)), constraints: BoxConstraints.loose(const Size(240, 400)),
child: FlowyButton( child: FlowyButton(
text: FlowyText.medium(fieldContext.name, fontSize: 12), text: FlowyText.medium(fieldContext.name, fontSize: 12),
hoverColor: theme.hover, hoverColor: theme.hover,

View File

@ -140,23 +140,23 @@ class _TrashPageState extends State<TrashPage> {
return SizedBox( return SizedBox(
height: 36, height: 36,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
FlowyText.semibold(LocaleKeys.trash_text.tr()), FlowyText.semibold(LocaleKeys.trash_text.tr()),
const Spacer(), const Spacer(),
SizedBox.fromSize( IntrinsicWidth(
size: const Size(102, 30),
child: FlowyButton( child: FlowyButton(
text: FlowyText.medium(LocaleKeys.trash_restoreAll.tr(), text: FlowyText.medium(LocaleKeys.trash_restoreAll.tr(),
fontSize: 12), fontSize: 12),
leftIcon: svgWidget('editor/restore', color: theme.iconColor), leftIcon: svgWidget('editor/restore', color: theme.iconColor),
hoverColor: theme.hover, hoverColor: theme.hover,
onTap: () => onTap: () => context.read<TrashBloc>().add(
context.read<TrashBloc>().add(const TrashEvent.restoreAll()), const TrashEvent.restoreAll(),
),
), ),
), ),
const HSpace(6), const HSpace(6),
SizedBox.fromSize( IntrinsicWidth(
size: const Size(102, 30),
child: FlowyButton( child: FlowyButton(
text: FlowyText.medium(LocaleKeys.trash_deleteAll.tr(), text: FlowyText.medium(LocaleKeys.trash_deleteAll.tr(),
fontSize: 12), fontSize: 12),

View File

@ -86,42 +86,6 @@ class _CreateTextFieldDialog extends State<NavigatorTextFieldDialog> {
} }
} }
class PopoverAlertView extends StatelessWidget {
final String title;
final void Function()? cancel;
final void Function()? confirm;
const PopoverAlertView({
required this.title,
this.confirm,
this.cancel,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return StyledDialog(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
...[
FlowyText.medium(title, color: theme.shader4),
],
if (confirm != null) ...[
const VSpace(20),
OkCancelButton(
onOkPressed: confirm,
onCancelPressed: cancel,
)
]
],
),
);
}
}
class NavigatorAlertDialog extends StatefulWidget { class NavigatorAlertDialog extends StatefulWidget {
final String title; final String title;
final void Function()? cancel; final void Function()? cancel;

View File

@ -5,14 +5,14 @@ import 'popover.dart';
/// If multiple popovers are exclusive, /// If multiple popovers are exclusive,
/// pass the same mutex to them. /// pass the same mutex to them.
class PopoverMutex { class PopoverMutex {
final ValueNotifier<PopoverState?> _stateNotifier = ValueNotifier(null); final _PopoverStateNotifier _stateNotifier = _PopoverStateNotifier();
PopoverMutex(); PopoverMutex();
void removePopoverStateListener(VoidCallback listener) { void removePopoverListener(VoidCallback listener) {
_stateNotifier.removeListener(listener); _stateNotifier.removeListener(listener);
} }
VoidCallback listenOnPopoverStateChanged(VoidCallback callback) { VoidCallback listenOnPopoverChanged(VoidCallback callback) {
listenerCallback() { listenerCallback() {
callback(); callback();
} }
@ -21,24 +21,31 @@ class PopoverMutex {
return listenerCallback; return listenerCallback;
} }
void close() { void close() => _stateNotifier.state?.close();
_stateNotifier.value?.close();
}
PopoverState? get state => _stateNotifier.value; PopoverState? get state => _stateNotifier.state;
set state(PopoverState? newState) { set state(PopoverState? newState) => _stateNotifier.state = newState;
if (_stateNotifier.value != null && _stateNotifier.value != newState) {
_stateNotifier.value?.close();
}
_stateNotifier.value = newState;
}
void removeState() { void removeState() {
_stateNotifier.value = null; _stateNotifier.state = null;
} }
void dispose() { void dispose() {
_stateNotifier.dispose(); _stateNotifier.dispose();
} }
} }
class _PopoverStateNotifier extends ChangeNotifier {
PopoverState? _state;
PopoverState? get state => _state;
set state(PopoverState? newState) {
if (_state != null && _state != newState) {
_state?.close();
}
_state = newState;
notifyListeners();
}
}

View File

@ -8,6 +8,7 @@ class FlowyText extends StatelessWidget {
final double fontSize; final double fontSize;
final FontWeight fontWeight; final FontWeight fontWeight;
final TextAlign? textAlign; final TextAlign? textAlign;
final int? maxLines;
final Color? color; final Color? color;
const FlowyText( const FlowyText(
@ -18,21 +19,40 @@ class FlowyText extends StatelessWidget {
this.fontWeight = FontWeight.w400, this.fontWeight = FontWeight.w400,
this.textAlign, this.textAlign,
this.color, this.color,
this.maxLines = 1,
}) : super(key: key); }) : super(key: key);
const FlowyText.semibold(this.title, const FlowyText.semibold(
{Key? key, this.fontSize = 16, this.overflow, this.color, this.textAlign}) this.title, {
: fontWeight = FontWeight.w600, Key? key,
this.fontSize = 16,
this.overflow,
this.color,
this.textAlign,
this.maxLines = 1,
}) : fontWeight = FontWeight.w600,
super(key: key); super(key: key);
const FlowyText.medium(this.title, const FlowyText.medium(
{Key? key, this.fontSize = 16, this.overflow, this.color, this.textAlign}) this.title, {
: fontWeight = FontWeight.w500, Key? key,
this.fontSize = 16,
this.overflow,
this.color,
this.textAlign,
this.maxLines = 1,
}) : fontWeight = FontWeight.w500,
super(key: key); super(key: key);
const FlowyText.regular(this.title, const FlowyText.regular(
{Key? key, this.fontSize = 16, this.overflow, this.color, this.textAlign}) this.title, {
: fontWeight = FontWeight.w400, Key? key,
this.fontSize = 16,
this.overflow,
this.color,
this.textAlign,
this.maxLines = 1,
}) : fontWeight = FontWeight.w400,
super(key: key); super(key: key);
@override @override
@ -40,6 +60,7 @@ class FlowyText extends StatelessWidget {
final theme = context.watch<AppTheme>(); final theme = context.watch<AppTheme>();
return Text( return Text(
title, title,
maxLines: maxLines,
textAlign: textAlign, textAlign: textAlign,
overflow: overflow ?? TextOverflow.clip, overflow: overflow ?? TextOverflow.clip,
style: TextStyle( style: TextStyle(