mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: allow fields to not wrap cell content (#5128)
This commit is contained in:
@ -113,7 +113,7 @@ List<CellContext> _makeCells(
|
||||
cellContexts.removeWhere((cellContext) {
|
||||
final fieldInfo = fieldController.getField(cellContext.fieldId);
|
||||
return fieldInfo == null ||
|
||||
!(fieldInfo.fieldSettings?.visibility.isVisibleState() ?? false) ||
|
||||
!(fieldInfo.visibility?.isVisibleState() ?? false) ||
|
||||
(groupFieldId != null && cellContext.fieldId == groupFieldId);
|
||||
});
|
||||
return cellContexts.toList();
|
||||
|
@ -41,7 +41,7 @@ class _DateCellState extends State<DateCardCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const DateCellEvent.initial());
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<DateCellBloc, DateCellState>(
|
||||
buildWhen: (previous, current) => previous.dateStr != current.dateStr,
|
||||
|
@ -41,7 +41,7 @@ class _NumberCellState extends State<NumberCardCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const NumberCellEvent.initial());
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<NumberCellBloc, NumberCellState>(
|
||||
buildWhen: (previous, current) => previous.content != current.content,
|
||||
|
@ -46,7 +46,7 @@ class _SelectOptionCellState extends State<SelectOptionCardCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const SelectOptionCellEvent.initial());
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<SelectOptionCellBloc, SelectOptionCellState>(
|
||||
buildWhen: (previous, current) {
|
||||
|
@ -52,7 +52,7 @@ class _TextCellState extends State<TextCardCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const TextCellEvent.initial());
|
||||
);
|
||||
late final TextEditingController _textEditingController =
|
||||
TextEditingController(text: cellBloc.state.content);
|
||||
final focusNode = SingleListenerFocusNode();
|
||||
|
@ -41,7 +41,7 @@ class _TimestampCellState extends State<TimestampCardCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const TimestampCellEvent.initial());
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<TimestampCellBloc, TimestampCellState>(
|
||||
buildWhen: (previous, current) => previous.dateStr != current.dateStr,
|
||||
|
@ -41,7 +41,7 @@ class _URLCellState extends State<URLCardCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const URLCellEvent.initial());
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<URLCellBloc, URLCellState>(
|
||||
buildWhen: (previous, current) => previous.content != current.content,
|
||||
|
@ -27,27 +27,15 @@ class DesktopGridDateCellSkin extends IEditableDateCellSkin {
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
constraints: BoxConstraints.loose(const Size(260, 620)),
|
||||
margin: EdgeInsets.zero,
|
||||
child: Container(
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: FlowyText.medium(
|
||||
state.dateStr,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: state.fieldInfo.wrapCellContent ?? false
|
||||
? _buildCellContent(state)
|
||||
: SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: _buildCellContent(state),
|
||||
),
|
||||
),
|
||||
if (state.data?.reminderId.isNotEmpty ?? false) ...[
|
||||
const HSpace(4),
|
||||
FlowyTooltip(
|
||||
message: LocaleKeys.grid_field_reminderOnDateTooltip.tr(),
|
||||
child: const FlowySvg(FlowySvgs.clock_alarm_s),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
popupBuilder: (BuildContext popoverContent) {
|
||||
return DateCellEditor(
|
||||
@ -60,4 +48,30 @@ class DesktopGridDateCellSkin extends IEditableDateCellSkin {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCellContent(DateCellState state) {
|
||||
final wrap = state.fieldInfo.wrapCellContent ?? false;
|
||||
return Padding(
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: FlowyText.medium(
|
||||
state.dateStr,
|
||||
overflow: wrap ? null : TextOverflow.ellipsis,
|
||||
maxLines: wrap ? null : 1,
|
||||
),
|
||||
),
|
||||
if (state.data?.reminderId.isNotEmpty ?? false) ...[
|
||||
const HSpace(4),
|
||||
FlowyTooltip(
|
||||
message: LocaleKeys.grid_field_reminderOnDateTooltip.tr(),
|
||||
child: const FlowySvg(FlowySvgs.clock_alarm_s),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/number_cell_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../editable_cell_skeleton/number.dart';
|
||||
|
||||
@ -19,7 +20,7 @@ class DesktopGridNumberCellSkin extends IEditableNumberCellSkin {
|
||||
focusNode: focusNode,
|
||||
onEditingComplete: () => focusNode.unfocus(),
|
||||
onSubmitted: (_) => focusNode.unfocus(),
|
||||
maxLines: null,
|
||||
maxLines: context.watch<NumberCellBloc>().state.wrap ? null : 1,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
textInputAction: TextInputAction.done,
|
||||
decoration: InputDecoration(
|
||||
|
@ -3,6 +3,7 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/relation_cell_editor.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/relation_cell_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
@ -32,13 +33,52 @@ class DesktopGridRelationCellSkin extends IEditableRelationCellSkin {
|
||||
child: const RelationCellEditor(),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: state.wrap
|
||||
? _buildWrapRows(context, state.rows)
|
||||
: _buildNoWrapRows(context, state.rows),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWrapRows(
|
||||
BuildContext context,
|
||||
List<RelatedRowDataPB> rows,
|
||||
) {
|
||||
return Padding(
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: Wrap(
|
||||
runSpacing: 4,
|
||||
spacing: 4.0,
|
||||
children: rows.map(
|
||||
(row) {
|
||||
final isEmpty = row.name.isEmpty;
|
||||
return FlowyText.medium(
|
||||
isEmpty ? LocaleKeys.grid_row_titlePlaceholder.tr() : row.name,
|
||||
color: isEmpty ? Theme.of(context).hintColor : null,
|
||||
decoration: TextDecoration.underline,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNoWrapRows(
|
||||
BuildContext context,
|
||||
List<RelatedRowDataPB> rows,
|
||||
) {
|
||||
return SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Padding(
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: Wrap(
|
||||
runSpacing: 4.0,
|
||||
spacing: 4.0,
|
||||
children: state.rows.map(
|
||||
child: SeparatedRow(
|
||||
separatorBuilder: () => const HSpace(4.0),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: rows.map(
|
||||
(row) {
|
||||
final isEmpty = row.name.isEmpty;
|
||||
return FlowyText.medium(
|
||||
|
@ -3,7 +3,7 @@ import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -23,6 +23,7 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
|
||||
controller: popoverController,
|
||||
constraints: const BoxConstraints.tightFor(width: 300),
|
||||
margin: EdgeInsets.zero,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
return SelectOptionCellEditor(
|
||||
@ -32,35 +33,67 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
|
||||
onClose: () => cellContainerNotifier.isFocus = false,
|
||||
child: BlocBuilder<SelectOptionCellBloc, SelectOptionCellState>(
|
||||
builder: (context, state) {
|
||||
return Container(
|
||||
return Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: state.selectedOptions.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: _buildOptions(context, state.selectedOptions),
|
||||
child: state.wrap
|
||||
? _buildWrapOptions(context, state.selectedOptions)
|
||||
: _buildNoWrapOptions(context, state.selectedOptions),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOptions(context, List<SelectOptionPB> options) {
|
||||
return Wrap(
|
||||
runSpacing: 4,
|
||||
children: options.map(
|
||||
(option) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
child: SelectOptionTag(
|
||||
option: option,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 1,
|
||||
horizontal: 8,
|
||||
Widget _buildWrapOptions(BuildContext context, List<SelectOptionPB> options) {
|
||||
return Padding(
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: Wrap(
|
||||
runSpacing: 4,
|
||||
children: options.map(
|
||||
(option) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
child: SelectOptionTag(
|
||||
option: option,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 1,
|
||||
horizontal: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNoWrapOptions(
|
||||
BuildContext context,
|
||||
List<SelectOptionPB> options,
|
||||
) {
|
||||
return SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Padding(
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: options.map(
|
||||
(option) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
child: SelectOptionTag(
|
||||
option: option,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 1,
|
||||
horizontal: 8,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class DesktopGridTextCellSkin extends IEditableTextCellSkin {
|
||||
child: TextField(
|
||||
controller: textEditingController,
|
||||
focusNode: focusNode,
|
||||
maxLines: null,
|
||||
maxLines: context.watch<TextCellBloc>().state.wrap ? null : 1,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
|
@ -16,10 +16,23 @@ class DesktopGridTimestampCellSkin extends IEditableTimestampCellSkin {
|
||||
) {
|
||||
return Container(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: state.wrap
|
||||
? _buildCellContent(state)
|
||||
: SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: _buildCellContent(state),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCellContent(TimestampCellState state) {
|
||||
return Padding(
|
||||
padding: GridSize.cellContentInsets,
|
||||
child: FlowyText.medium(
|
||||
state.dateStr,
|
||||
maxLines: null,
|
||||
overflow: state.wrap ? null : TextOverflow.ellipsis,
|
||||
maxLines: state.wrap ? null : 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../editable_cell_skeleton/url.dart';
|
||||
|
||||
@ -24,28 +25,31 @@ class DesktopGridURLSkin extends IEditableURLCellSkin {
|
||||
TextEditingController textEditingController,
|
||||
URLCellDataNotifier cellDataNotifier,
|
||||
) {
|
||||
return TextField(
|
||||
controller: textEditingController,
|
||||
focusNode: focusNode,
|
||||
maxLines: null,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: GridSize.cellContentInsets,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
disabledBorder: InputBorder.none,
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: Theme.of(context).hintColor),
|
||||
isDense: true,
|
||||
return BlocSelector<URLCellBloc, URLCellState, bool>(
|
||||
selector: (state) => state.wrap,
|
||||
builder: (context, wrap) => TextField(
|
||||
controller: textEditingController,
|
||||
focusNode: focusNode,
|
||||
maxLines: wrap ? null : 1,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: GridSize.cellContentInsets,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
disabledBorder: InputBorder.none,
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: Theme.of(context).hintColor),
|
||||
isDense: true,
|
||||
),
|
||||
onTapOutside: (_) => focusNode.unfocus(),
|
||||
),
|
||||
onTapOutside: (_) => focusNode.unfocus(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -179,8 +183,7 @@ class _VisitURLAccessoryState extends State<_VisitURLAccessory>
|
||||
bool enable() => widget.cellDataNotifier.value.isNotEmpty;
|
||||
|
||||
@override
|
||||
void onTap() =>
|
||||
openUrlCellLink(widget.cellDataNotifier.value);
|
||||
void onTap() => openUrlCellLink(widget.cellDataNotifier.value);
|
||||
}
|
||||
|
||||
class _URLAccessoryIconContainer extends StatelessWidget {
|
||||
@ -190,9 +193,8 @@ class _URLAccessoryIconContainer extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 26,
|
||||
height: 26,
|
||||
return SizedBox.square(
|
||||
dimension: 26,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: child,
|
||||
|
@ -57,7 +57,7 @@ class _DateCellState extends GridCellState<EditableDateCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const DateCellEvent.initial());
|
||||
);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
@ -58,7 +58,7 @@ class _NumberCellState extends GridEditableTextCell<EditableNumberCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const NumberCellEvent.initial());
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -81,12 +81,16 @@ class _NumberCellState extends GridEditableTextCell<EditableNumberCell> {
|
||||
child: BlocListener<NumberCellBloc, NumberCellState>(
|
||||
listener: (context, state) =>
|
||||
_textEditingController.text = state.content,
|
||||
child: widget.skin.build(
|
||||
context,
|
||||
widget.cellContainerNotifier,
|
||||
cellBloc,
|
||||
focusNode,
|
||||
_textEditingController,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return widget.skin.build(
|
||||
context,
|
||||
widget.cellContainerNotifier,
|
||||
cellBloc,
|
||||
focusNode,
|
||||
_textEditingController,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -64,7 +64,7 @@ class _SelectOptionCellState extends GridCellState<EditableSelectOptionCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const SelectOptionCellEvent.initial());
|
||||
);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
@ -58,7 +58,7 @@ class _TextCellState extends GridEditableTextCell<EditableTextCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const TextCellEvent.initial());
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -82,12 +82,16 @@ class _TextCellState extends GridEditableTextCell<EditableTextCell> {
|
||||
listener: (context, state) {
|
||||
_textEditingController.text = state.content;
|
||||
},
|
||||
child: widget.skin.build(
|
||||
context,
|
||||
widget.cellContainerNotifier,
|
||||
cellBloc,
|
||||
focusNode,
|
||||
_textEditingController,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return widget.skin.build(
|
||||
context,
|
||||
widget.cellContainerNotifier,
|
||||
cellBloc,
|
||||
focusNode,
|
||||
_textEditingController,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -57,7 +57,7 @@ class _TimestampCellState extends GridCellState<EditableTimestampCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const TimestampCellEvent.initial());
|
||||
);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
@ -85,7 +85,7 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
|
||||
widget.databaseController,
|
||||
widget.cellContext,
|
||||
).as(),
|
||||
)..add(const URLCellEvent.initial());
|
||||
);
|
||||
|
||||
@override
|
||||
SingleListenerFocusNode focusNode = SingleListenerFocusNode();
|
||||
|
@ -42,7 +42,7 @@ class _DateCellEditor extends State<DateCellEditor> {
|
||||
create: (context) => DateCellEditorBloc(
|
||||
reminderBloc: getIt<ReminderBloc>(),
|
||||
cellController: widget.cellController,
|
||||
)..add(const DateCellEditorEvent.initial()),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: BlocBuilder<DateCellEditorBloc, DateCellEditorState>(
|
||||
|
@ -1,7 +1,5 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
@ -12,12 +10,14 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import 'field_type_list.dart';
|
||||
import 'type_option_editor/builder.dart';
|
||||
@ -87,10 +87,12 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FieldNameTextField(
|
||||
padding: const EdgeInsets.fromLTRB(4, 4, 4, 8),
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
||||
textEditingController: textController,
|
||||
),
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
_EditFieldButton(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
onTap: () {
|
||||
setState(() => _currentPage = FieldEditorPage.details);
|
||||
},
|
||||
@ -107,8 +109,11 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
_actionCell(FieldAction.clearData),
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
_actionCell(FieldAction.delete),
|
||||
const TypeOptionSeparator(spacing: 8.0),
|
||||
_actionCell(FieldAction.wrap),
|
||||
const VSpace(8.0),
|
||||
],
|
||||
).padding(all: 8.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -124,24 +129,32 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
|
||||
Widget _actionCell(FieldAction action) {
|
||||
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
|
||||
builder: (context, state) => FieldActionCell(
|
||||
viewId: widget.viewId,
|
||||
fieldInfo: state.field,
|
||||
action: action,
|
||||
builder: (context, state) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: FieldActionCell(
|
||||
viewId: widget.viewId,
|
||||
fieldInfo: state.field,
|
||||
action: action,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EditFieldButton extends StatelessWidget {
|
||||
const _EditFieldButton({this.onTap});
|
||||
const _EditFieldButton({
|
||||
required this.padding,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
final EdgeInsetsGeometry padding;
|
||||
final void Function()? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
return Container(
|
||||
height: GridSize.popoverItemHeight,
|
||||
padding: padding,
|
||||
child: FlowyButton(
|
||||
leftIcon: const FlowySvg(FlowySvgs.edit_s),
|
||||
text: FlowyText.medium(
|
||||
@ -184,10 +197,11 @@ class FieldActionCell extends StatelessWidget {
|
||||
),
|
||||
onHover: (_) => popoverMutex?.close(),
|
||||
onTap: () => action.run(context, viewId, fieldInfo),
|
||||
leftIcon: action.icon(
|
||||
leftIcon: action.leading(
|
||||
fieldInfo,
|
||||
enable ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
rightIcon: action.trailing(context, fieldInfo),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -198,10 +212,11 @@ enum FieldAction {
|
||||
toggleVisibility,
|
||||
duplicate,
|
||||
clearData,
|
||||
delete;
|
||||
delete,
|
||||
wrap;
|
||||
|
||||
Widget icon(FieldInfo fieldInfo, Color? color) {
|
||||
late final FlowySvgData svgData;
|
||||
Widget? leading(FieldInfo fieldInfo, Color? color) {
|
||||
FlowySvgData? svgData;
|
||||
switch (this) {
|
||||
case FieldAction.insertLeft:
|
||||
svgData = FlowySvgs.arrow_s;
|
||||
@ -220,6 +235,11 @@ enum FieldAction {
|
||||
svgData = FlowySvgs.reload_s;
|
||||
case FieldAction.delete:
|
||||
svgData = FlowySvgs.delete_s;
|
||||
default:
|
||||
}
|
||||
|
||||
if (svgData == null) {
|
||||
return null;
|
||||
}
|
||||
final icon = FlowySvg(
|
||||
svgData,
|
||||
@ -231,6 +251,21 @@ enum FieldAction {
|
||||
: icon;
|
||||
}
|
||||
|
||||
Widget? trailing(BuildContext context, FieldInfo fieldInfo) {
|
||||
if (this == FieldAction.wrap) {
|
||||
return Toggle(
|
||||
value: fieldInfo.wrapCellContent ?? false,
|
||||
onChanged: (_) => context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(const FieldEditorEvent.toggleWrapCellContent()),
|
||||
style: ToggleStyle.big,
|
||||
padding: EdgeInsets.zero,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
String title(FieldInfo fieldInfo) {
|
||||
switch (this) {
|
||||
case FieldAction.insertLeft:
|
||||
@ -250,6 +285,8 @@ enum FieldAction {
|
||||
return LocaleKeys.grid_field_clear.tr();
|
||||
case FieldAction.delete:
|
||||
return LocaleKeys.grid_field_delete.tr();
|
||||
case FieldAction.wrap:
|
||||
return LocaleKeys.grid_field_wrap.tr();
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,6 +345,11 @@ enum FieldAction {
|
||||
).show(context);
|
||||
PopoverContainer.of(context).close();
|
||||
break;
|
||||
case FieldAction.wrap:
|
||||
context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(const FieldEditorEvent.toggleWrapCellContent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ class _GridCellEnterRegion extends StatelessWidget {
|
||||
onExit: (p) =>
|
||||
CellContainerNotifier.of(context, listen: false).isHover = false,
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
alignment: Alignment.center,
|
||||
fit: StackFit.expand,
|
||||
children: children,
|
||||
),
|
||||
|
Reference in New Issue
Block a user