mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: adjust board card UI (#3954)
* chore: adjust board card UI * chore: fix accessory popover and add title placeholder * chore: add pubspec.lock * chore: fix integration test * chore: apply suggestions from code review Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> * chore: apply suggestions from Mathias --------- Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
This commit is contained in:
parent
bddaac05ae
commit
e18e031710
@ -1,5 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/presentation/widgets/board_column_header.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/card/container/card_container.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
@ -15,8 +16,8 @@ const defaultLastCardName = 'Card 3';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board add row test', () {
|
||||
testWidgets('Add card from header', (tester) async {
|
||||
group('board add row test:', () {
|
||||
testWidgets('from header', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -45,7 +46,7 @@ void main() {
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(IntrinsicHeight),
|
||||
of: find.byType(RowCardContainer),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
@ -59,7 +60,7 @@ void main() {
|
||||
expect(firstCardText.text, newCardName);
|
||||
});
|
||||
|
||||
testWidgets('Add card from footer', (tester) async {
|
||||
testWidgets('from footer', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
@ -87,7 +88,7 @@ void main() {
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(IntrinsicHeight),
|
||||
of: find.byType(RowCardContainer),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
|
@ -17,6 +17,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/material.dart' hide Card;
|
||||
@ -159,10 +160,12 @@ class _BoardContentState extends State<BoardContent> {
|
||||
boardScrollController: scrollManager,
|
||||
scrollController: scrollController,
|
||||
controller: context.read<BoardBloc>().boardController,
|
||||
groupConstraints: const BoxConstraints.tightFor(width: 300),
|
||||
groupConstraints: const BoxConstraints.tightFor(width: 256),
|
||||
config: const AppFlowyBoardConfig(
|
||||
groupPadding: EdgeInsets.symmetric(horizontal: 4),
|
||||
groupItemPadding: EdgeInsets.symmetric(horizontal: 4),
|
||||
footerPadding: EdgeInsets.fromLTRB(4, 14, 4, 4),
|
||||
stretchGroupHeight: false,
|
||||
),
|
||||
leading: HiddenGroupsColumn(margin: config.headerPadding),
|
||||
trailing: showCreateGroupButton
|
||||
@ -200,20 +203,15 @@ class _BoardContentState extends State<BoardContent> {
|
||||
|
||||
Widget _buildFooter(BuildContext context, AppFlowyGroupData columnData) {
|
||||
return AppFlowyGroupFooter(
|
||||
height: 50,
|
||||
height: 36,
|
||||
margin: config.footerPadding,
|
||||
icon: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: FlowySvg(
|
||||
FlowySvgs.add_s,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.add_s,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
title: FlowyText.medium(
|
||||
LocaleKeys.board_column_createNewCard.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onAddButtonClick: () => context
|
||||
.read<BoardBloc>()
|
||||
@ -266,6 +264,14 @@ class _BoardContentState extends State<BoardContent> {
|
||||
rowMeta: rowMeta,
|
||||
rowCache: rowCache,
|
||||
),
|
||||
styleConfiguration: RowCardStyleConfiguration(
|
||||
hoverStyle: HoverStyle(
|
||||
hoverColor: Theme.of(context).brightness == Brightness.light
|
||||
? const Color(0x0F1F2329)
|
||||
: const Color(0x0FEFF4FB),
|
||||
foregroundColorOnHover: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
onStartEditing: () => boardBloc
|
||||
.add(BoardEvent.startEditingRow(groupData.group, groupItem.row)),
|
||||
onEndEditing: () =>
|
||||
@ -280,8 +286,10 @@ class _BoardContentState extends State<BoardContent> {
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
border: Border.fromBorderSide(
|
||||
BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.4,
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? const Color(0xFF1F2329).withOpacity(0.12)
|
||||
: const Color(0xFF59647A),
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
boxShadow: [
|
||||
|
@ -3,7 +3,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/application/board_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/card/define.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
@ -158,10 +157,8 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
hoverColor: Colors.transparent,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding + 4,
|
||||
horizontal: 8,
|
||||
),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
|
@ -73,9 +73,9 @@ class RowCard<CustomCardData> extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RowCardState<T> extends State<RowCard<T>> {
|
||||
final popoverController = PopoverController();
|
||||
late final CardBloc _cardBloc;
|
||||
late final EditableRowNotifier rowNotifier;
|
||||
late final PopoverController popoverController;
|
||||
AccessoryType? accessoryType;
|
||||
|
||||
@override
|
||||
@ -100,8 +100,6 @@ class _RowCardState<T> extends State<RowCard<T>> {
|
||||
widget.onEndEditing();
|
||||
}
|
||||
});
|
||||
|
||||
popoverController = PopoverController();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -111,26 +109,21 @@ class _RowCardState<T> extends State<RowCard<T>> {
|
||||
child: BlocBuilder<CardBloc, RowCardState>(
|
||||
buildWhen: (previous, current) {
|
||||
// Rebuild when:
|
||||
// 1.If the length of the cells is not the same
|
||||
// 2.isEditing changed
|
||||
// 1. If the length of the cells is not the same or isEditing changed
|
||||
if (previous.cells.length != current.cells.length ||
|
||||
previous.isEditing != current.isEditing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3.Compare the content of the cells. The cells consists of
|
||||
// list of [BoardCellEquatable] that extends the [Equatable].
|
||||
// 2. the content of the cells changed
|
||||
return !listEquals(previous.cells, current.cells);
|
||||
},
|
||||
builder: (context, state) {
|
||||
// mobile
|
||||
if (PlatformExtension.isMobile) {
|
||||
// TODO(yijing): refactor it in mobile to display card in database view
|
||||
return RowCardContainer(
|
||||
buildAccessoryWhen: () => state.isEditing == false,
|
||||
accessoryBuilder: (context) {
|
||||
return [];
|
||||
},
|
||||
accessories: const [],
|
||||
openAccessory: (p0) {},
|
||||
openCard: (context) => widget.openCard(context),
|
||||
child: _CardContent<T>(
|
||||
@ -143,29 +136,27 @@ class _RowCardState<T> extends State<RowCard<T>> {
|
||||
),
|
||||
);
|
||||
}
|
||||
// desktop
|
||||
|
||||
return AppFlowyPopover(
|
||||
controller: popoverController,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
constraints: BoxConstraints.loose(const Size(140, 200)),
|
||||
margin: const EdgeInsets.all(6),
|
||||
direction: PopoverDirection.rightWithCenterAligned,
|
||||
popupBuilder: (popoverContext) => _handlePopoverBuilder(
|
||||
context,
|
||||
popoverContext,
|
||||
),
|
||||
popupBuilder: (popoverContext) {
|
||||
return RowActions(
|
||||
viewId: _cardBloc.viewId,
|
||||
rowId: _cardBloc.rowMeta.id,
|
||||
groupId: widget.groupId,
|
||||
);
|
||||
},
|
||||
child: RowCardContainer(
|
||||
buildAccessoryWhen: () => state.isEditing == false,
|
||||
accessoryBuilder: (context) {
|
||||
if (widget.styleConfiguration.showAccessory == false) {
|
||||
return [];
|
||||
} else {
|
||||
return [
|
||||
_CardEditOption(rowNotifier: rowNotifier),
|
||||
CardMoreOption(),
|
||||
];
|
||||
}
|
||||
},
|
||||
accessories: [
|
||||
if (widget.styleConfiguration.showAccessory) ...[
|
||||
_CardEditOption(rowNotifier: rowNotifier),
|
||||
const CardMoreOption(),
|
||||
],
|
||||
],
|
||||
openAccessory: _handleOpenAccessory,
|
||||
openCard: (context) => widget.openCard(context),
|
||||
child: _CardContent<T>(
|
||||
@ -194,22 +185,6 @@ class _RowCardState<T> extends State<RowCard<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _handlePopoverBuilder(
|
||||
BuildContext context,
|
||||
BuildContext popoverContext,
|
||||
) {
|
||||
switch (accessoryType!) {
|
||||
case AccessoryType.edit:
|
||||
throw UnimplementedError();
|
||||
case AccessoryType.more:
|
||||
return RowActions(
|
||||
viewId: context.read<CardBloc>().viewId,
|
||||
rowId: context.read<CardBloc>().rowMeta.id,
|
||||
groupId: widget.groupId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
rowNotifier.dispose();
|
||||
@ -241,6 +216,7 @@ class _CardContent<CustomCardData> extends StatelessWidget {
|
||||
if (styleConfiguration.hoverStyle != null) {
|
||||
return FlowyHover(
|
||||
style: styleConfiguration.hoverStyle,
|
||||
buildWhenOnHover: () => !rowNotifier.isEditing.value,
|
||||
child: Padding(
|
||||
padding: styleConfiguration.cardPadding,
|
||||
child: Column(
|
||||
@ -296,21 +272,21 @@ class _CardContent<CustomCardData> extends StatelessWidget {
|
||||
}
|
||||
|
||||
class CardMoreOption extends StatelessWidget with CardAccessory {
|
||||
CardMoreOption({Key? key}) : super(key: key);
|
||||
const CardMoreOption({super.key});
|
||||
|
||||
@override
|
||||
AccessoryType get type => AccessoryType.more;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: FlowySvg(
|
||||
FlowySvgs.details_s,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
FlowySvgs.three_dots_s,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AccessoryType get type => AccessoryType.more;
|
||||
}
|
||||
|
||||
class _CardEditOption extends StatelessWidget with CardAccessory {
|
||||
@ -326,7 +302,7 @@ class _CardEditOption extends StatelessWidget with CardAccessory {
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: FlowySvg(
|
||||
FlowySvgs.edit_s,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -346,7 +322,7 @@ class RowCardStyleConfiguration {
|
||||
|
||||
const RowCardStyleConfiguration({
|
||||
this.showAccessory = true,
|
||||
this.cellPadding = const EdgeInsets.only(left: 4, right: 4),
|
||||
this.cellPadding = EdgeInsets.zero,
|
||||
this.cardPadding = const EdgeInsets.all(8),
|
||||
this.hoverStyle,
|
||||
});
|
||||
|
@ -5,6 +5,7 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class CheckboxCardCell extends CardCell {
|
||||
@ -24,11 +25,11 @@ class _CheckboxCellState extends State<CheckboxCardCell> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as CheckboxCellController;
|
||||
_cellBloc = CheckboxCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const CheckboxCellEvent.initial());
|
||||
super.initState();
|
||||
_cellBloc = CheckboxCellBloc(cellController: cellController)
|
||||
..add(const CheckboxCellEvent.initial());
|
||||
}
|
||||
|
||||
@override
|
||||
@ -47,7 +48,7 @@ class _CheckboxCellState extends State<CheckboxCardCell> {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: FlowyIconButton(
|
||||
iconPadding: EdgeInsets.zero,
|
||||
icon: icon,
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../row/cells/checklist_cell/checklist_cell_bloc.dart';
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class ChecklistCardCell extends CardCell {
|
||||
@ -37,7 +38,7 @@ class _ChecklistCellState extends State<ChecklistCardCell> {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: ChecklistProgressBar(
|
||||
tasks: state.tasks,
|
||||
percent: state.percent,
|
||||
|
@ -42,31 +42,28 @@ class _DateCellState extends State<DateCardCell> {
|
||||
buildWhen: (previous, current) => previous.dateStr != current.dateStr,
|
||||
builder: (context, state) {
|
||||
if (state.dateStr.isEmpty) {
|
||||
return const SizedBox();
|
||||
} else {
|
||||
final Widget? custom = widget.renderHook?.call(
|
||||
state.data,
|
||||
widget.cardData,
|
||||
context,
|
||||
);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: FlowyText.regular(
|
||||
state.dateStr,
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final Widget? custom = widget.renderHook?.call(
|
||||
state.data,
|
||||
widget.cardData,
|
||||
context,
|
||||
);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: FlowyText.regular(
|
||||
state.dateStr,
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -52,30 +52,28 @@ class _NumberCellState extends State<NumberCardCell> {
|
||||
previous.cellContent != current.cellContent,
|
||||
builder: (context, state) {
|
||||
if (state.cellContent.isEmpty) {
|
||||
return const SizedBox();
|
||||
} else {
|
||||
final Widget? custom = widget.renderHook?.call(
|
||||
state.cellContent,
|
||||
widget.cardData,
|
||||
context,
|
||||
);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: FlowyText.medium(
|
||||
state.cellContent,
|
||||
fontSize: widget.style?.fontSize ?? 14,
|
||||
),
|
||||
),
|
||||
);
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final Widget? custom = widget.renderHook?.call(
|
||||
state.cellContent,
|
||||
widget.cardData,
|
||||
context,
|
||||
);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: FlowyText.regular(
|
||||
state.cellContent,
|
||||
fontSize: widget.style?.fontSize ?? 11,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart'
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
|
||||
class SelectOptionCardCellStyle extends CardCellStyle {}
|
||||
@ -69,12 +70,11 @@ class _SelectOptionCellState extends State<SelectOptionCardCell> {
|
||||
)
|
||||
.toList();
|
||||
|
||||
return IntrinsicHeight(
|
||||
return Align(
|
||||
alignment: AlignmentDirectional.topStart,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: SizedBox.expand(
|
||||
child: Wrap(spacing: 4, runSpacing: 2, children: children),
|
||||
),
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: Wrap(spacing: 4, runSpacing: 2, children: children),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1,10 +1,13 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/text_cell/text_cell_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../row/cell_builder.dart';
|
||||
import '../define.dart';
|
||||
import 'card_cell.dart';
|
||||
@ -120,24 +123,30 @@ class _TextCellState extends State<TextCardCell> {
|
||||
return custom;
|
||||
}
|
||||
|
||||
final isTitle =
|
||||
context.read<TextCellBloc>().cellController.fieldInfo.isPrimary;
|
||||
if (state.content.isEmpty &&
|
||||
state.enableEdit == false &&
|
||||
focusWhenInit == false) {
|
||||
return const SizedBox();
|
||||
focusWhenInit == false &&
|
||||
!isTitle) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final child = state.enableEdit || focusWhenInit
|
||||
? _buildTextField()
|
||||
: _buildText(state);
|
||||
: _buildText(state, isTitle);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
if (widget.showNotes) ...[
|
||||
const FlowySvg(FlowySvgs.notes_s),
|
||||
const HSpace(4),
|
||||
return Padding(
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.showNotes) ...[
|
||||
const FlowySvg(FlowySvgs.notes_s),
|
||||
const HSpace(4),
|
||||
],
|
||||
Expanded(child: child),
|
||||
],
|
||||
Expanded(child: child),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -157,47 +166,46 @@ class _TextCellState extends State<TextCardCell> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
double _fontSize() {
|
||||
if (widget.style != null) {
|
||||
return widget.style!.fontSize;
|
||||
}
|
||||
|
||||
return 14;
|
||||
}
|
||||
|
||||
Widget _buildText(TextCellState state) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: FlowyText.medium(
|
||||
state.content,
|
||||
fontSize: _fontSize(),
|
||||
maxLines: null, // Enable multiple lines
|
||||
),
|
||||
Widget _buildText(TextCellState state, bool isTitle) {
|
||||
final text = state.content.isEmpty
|
||||
? LocaleKeys.grid_row_titlePlaceholder.tr()
|
||||
: state.content;
|
||||
final color = state.content.isEmpty ? Theme.of(context).hintColor : null;
|
||||
return FlowyText(
|
||||
text,
|
||||
fontSize: _fontSize(isTitle),
|
||||
fontWeight: _fontWeight(isTitle),
|
||||
color: color,
|
||||
maxLines: null, // Enable multiple lines
|
||||
);
|
||||
}
|
||||
|
||||
double _fontSize(bool isTitle) {
|
||||
return widget.style?.fontSize ?? (isTitle ? 12 : 11);
|
||||
}
|
||||
|
||||
FontWeight _fontWeight(bool isTitle) {
|
||||
return isTitle ? FontWeight.w500 : FontWeight.w400;
|
||||
}
|
||||
|
||||
Widget _buildTextField() {
|
||||
return IntrinsicHeight(
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: focusNode,
|
||||
onChanged: (value) => focusChanged(),
|
||||
onEditingComplete: () => focusNode.unfocus(),
|
||||
maxLines: null,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(fontSize: _fontSize()),
|
||||
decoration: InputDecoration(
|
||||
// Magic number 4 makes the textField take up the same space as FlowyText
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding + 4,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
),
|
||||
return TextField(
|
||||
controller: _controller,
|
||||
focusNode: focusNode,
|
||||
onChanged: (value) => focusChanged(),
|
||||
onEditingComplete: () => focusNode.unfocus(),
|
||||
maxLines: null,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(fontSize: _fontSize(true)),
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(vertical: CardSizes.cardCellPadding.top),
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
isCollapsed: true,
|
||||
hintText: LocaleKeys.grid_row_titlePlaceholder.tr(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -56,12 +56,10 @@ class _TimestampCellState extends State<TimestampCardCell> {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: FlowyText.regular(
|
||||
state.dateStr,
|
||||
fontSize: 13,
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/url_cell/url_cell_bloc.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@ -48,27 +47,24 @@ class _URLCellState extends State<URLCardCell> {
|
||||
builder: (context, state) {
|
||||
if (state.content.isEmpty) {
|
||||
return const SizedBox();
|
||||
} else {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: CardSizes.cardCellVPadding,
|
||||
),
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
text: state.content,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: widget.style?.fontSize ?? FontSizes.s14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
}
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: CardSizes.cardCellPadding,
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
text: state.content,
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
fontSize: widget.style?.fontSize ?? 11,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -26,7 +26,7 @@ class CardAccessoryContainer extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final children = accessories.map((accessory) {
|
||||
final children = accessories.map<Widget>((accessory) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
@ -36,32 +36,62 @@ class CardAccessoryContainer extends StatelessWidget {
|
||||
child: _wrapHover(context, accessory),
|
||||
);
|
||||
}).toList();
|
||||
return _wrapDecoration(context, Row(children: children));
|
||||
|
||||
children.insert(
|
||||
1,
|
||||
VerticalDivider(
|
||||
width: 1,
|
||||
thickness: 1,
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? const Color(0xFF1F2329).withOpacity(0.12)
|
||||
: const Color(0xff59647a),
|
||||
),
|
||||
);
|
||||
|
||||
return _wrapDecoration(
|
||||
context,
|
||||
IntrinsicHeight(child: Row(children: children)),
|
||||
);
|
||||
}
|
||||
|
||||
FlowyHover _wrapHover(BuildContext context, CardAccessory accessory) {
|
||||
return FlowyHover(
|
||||
style: HoverStyle(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.zero,
|
||||
),
|
||||
builder: (_, onHover) => SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
Widget _wrapHover(BuildContext context, CardAccessory accessory) {
|
||||
return SizedBox(
|
||||
width: 24,
|
||||
height: 22,
|
||||
child: FlowyHover(
|
||||
style: HoverStyle(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.zero,
|
||||
),
|
||||
child: accessory,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _wrapDecoration(BuildContext context, Widget child) {
|
||||
final borderSide = BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0,
|
||||
);
|
||||
final decoration = BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
border: Border.fromBorderSide(borderSide),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
border: Border.fromBorderSide(
|
||||
BorderSide(
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? const Color(0xFF1F2329).withOpacity(0.12)
|
||||
: const Color(0xff59647a),
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 4,
|
||||
spreadRadius: 0,
|
||||
color: const Color(0xFF1F2329).withOpacity(0.02),
|
||||
),
|
||||
BoxShadow(
|
||||
blurRadius: 4,
|
||||
spreadRadius: -2,
|
||||
color: const Color(0xFF1F2329).withOpacity(0.02),
|
||||
),
|
||||
],
|
||||
);
|
||||
return Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
|
@ -5,7 +5,7 @@ import 'accessory.dart';
|
||||
|
||||
class RowCardContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
final CardAccessoryBuilder? accessoryBuilder;
|
||||
final List<CardAccessory> accessories;
|
||||
final bool Function()? buildAccessoryWhen;
|
||||
final void Function(BuildContext) openCard;
|
||||
final void Function(AccessoryType) openAccessory;
|
||||
@ -13,7 +13,7 @@ class RowCardContainer extends StatelessWidget {
|
||||
required this.child,
|
||||
required this.openCard,
|
||||
required this.openAccessory,
|
||||
this.accessoryBuilder,
|
||||
required this.accessories,
|
||||
this.buildAccessoryWhen,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -30,15 +30,12 @@ class RowCardContainer extends StatelessWidget {
|
||||
shouldBuildAccessory = buildAccessoryWhen!.call();
|
||||
}
|
||||
|
||||
if (accessoryBuilder != null && shouldBuildAccessory) {
|
||||
final accessories = accessoryBuilder!(context);
|
||||
if (accessories.isNotEmpty) {
|
||||
container = _CardEnterRegion(
|
||||
accessories: accessories,
|
||||
onTapAccessory: openAccessory,
|
||||
child: container,
|
||||
);
|
||||
}
|
||||
if (shouldBuildAccessory && accessories.isNotEmpty) {
|
||||
container = _CardEnterRegion(
|
||||
accessories: accessories,
|
||||
onTapAccessory: openAccessory,
|
||||
child: container,
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
@ -75,8 +72,8 @@ class _CardEnterRegion extends StatelessWidget {
|
||||
if (onEnter) {
|
||||
children.add(
|
||||
Positioned(
|
||||
top: 8.0,
|
||||
right: 8.0,
|
||||
top: 10.0,
|
||||
right: 10.0,
|
||||
child: CardAccessoryContainer(
|
||||
accessories: accessories,
|
||||
onTapAccessory: onTapAccessory,
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class CardSizes {
|
||||
static double get cardCellVPadding => 6;
|
||||
static EdgeInsets get cardCellPadding => const EdgeInsets.all(4);
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class GridChecklistCellState extends GridCellState<GridChecklistCell> {
|
||||
: LocaleKeys.grid_checklist_hideComplete.tr(),
|
||||
width: 32,
|
||||
iconColorOnHover:
|
||||
Theme.of(context).colorScheme.onPrimary,
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
icon: FlowySvg(
|
||||
showIncompleteOnly
|
||||
? FlowySvgs.show_m
|
||||
|
@ -106,7 +106,7 @@ class SelectOptionTag extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: FlowyText.medium(
|
||||
child: FlowyText.regular(
|
||||
name,
|
||||
fontSize: FontSizes.s11,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@ -135,11 +135,11 @@ class SelectOptionTagCell extends StatelessWidget {
|
||||
final void Function(SelectOptionPB) onSelected;
|
||||
final SelectOptionPB option;
|
||||
const SelectOptionTagCell({
|
||||
super.key,
|
||||
required this.option,
|
||||
required this.onSelected,
|
||||
this.children = const [],
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -17,5 +17,6 @@ export 'style_widget/button.dart';
|
||||
export 'style_widget/icon_button.dart';
|
||||
export 'style_widget/scrolling/styled_scroll_bar.dart';
|
||||
export '/widget/spacing.dart';
|
||||
export '/widget/separated_flex.dart';
|
||||
export 'style_widget/scrolling/styled_list.dart';
|
||||
export 'style_widget/color_picker.dart';
|
||||
|
@ -0,0 +1,47 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef SeparatorBuilder = Widget Function();
|
||||
|
||||
class SeparatedColumn extends Column {
|
||||
SeparatedColumn({
|
||||
super.key,
|
||||
super.mainAxisAlignment,
|
||||
super.crossAxisAlignment,
|
||||
super.mainAxisSize,
|
||||
super.textBaseline,
|
||||
super.textDirection,
|
||||
super.verticalDirection,
|
||||
required SeparatorBuilder separatorBuilder,
|
||||
required List<Widget> children,
|
||||
}) : super(children: _insertSeparators(children, separatorBuilder));
|
||||
}
|
||||
|
||||
class SeparatedRow extends Row {
|
||||
SeparatedRow({
|
||||
super.key,
|
||||
super.mainAxisAlignment,
|
||||
super.crossAxisAlignment,
|
||||
super.mainAxisSize,
|
||||
super.textBaseline,
|
||||
super.textDirection,
|
||||
super.verticalDirection,
|
||||
required SeparatorBuilder separatorBuilder,
|
||||
required List<Widget> children,
|
||||
}) : super(children: _insertSeparators(children, separatorBuilder));
|
||||
}
|
||||
|
||||
List<Widget> _insertSeparators(
|
||||
List<Widget> children,
|
||||
SeparatorBuilder separatorBuilder,
|
||||
) {
|
||||
if (children.length < 2) {
|
||||
return children;
|
||||
}
|
||||
|
||||
List<Widget> newChildren = [];
|
||||
for (int i = 0; i < children.length - 1; i++) {
|
||||
newChildren.add(children[i]);
|
||||
newChildren.add(separatorBuilder());
|
||||
}
|
||||
return newChildren..add(children.last);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef SeparatorBuilder = Widget Function();
|
||||
|
||||
class SeparatedColumn extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
final SeparatorBuilder? separatorBuilder;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
final CrossAxisAlignment crossAxisAlignment;
|
||||
final MainAxisSize mainAxisSize;
|
||||
final TextBaseline? textBaseline;
|
||||
final TextDirection? textDirection;
|
||||
final VerticalDirection verticalDirection;
|
||||
|
||||
const SeparatedColumn({
|
||||
Key? key,
|
||||
required this.children,
|
||||
this.separatorBuilder,
|
||||
this.mainAxisAlignment = MainAxisAlignment.start,
|
||||
this.crossAxisAlignment = CrossAxisAlignment.center,
|
||||
this.mainAxisSize = MainAxisSize.max,
|
||||
this.verticalDirection = VerticalDirection.down,
|
||||
this.textBaseline,
|
||||
this.textDirection,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var c = children.toList();
|
||||
for (var i = c.length; i-- > 0;) {
|
||||
if (i > 0 && separatorBuilder != null) c.insert(i, separatorBuilder!());
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: mainAxisAlignment,
|
||||
crossAxisAlignment: crossAxisAlignment,
|
||||
mainAxisSize: mainAxisSize,
|
||||
textBaseline: textBaseline,
|
||||
textDirection: textDirection,
|
||||
verticalDirection: verticalDirection,
|
||||
children: c,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user