mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: enable delete field in edit row detail page
This commit is contained in:
parent
988e8db798
commit
34275664b2
@ -191,7 +191,8 @@
|
||||
"optionTitle": "Options",
|
||||
"addOption": "Add option",
|
||||
"editProperty": "Edit property",
|
||||
"newColumn": "New column"
|
||||
"newColumn": "New column",
|
||||
"deleteFieldPromptMessage": "Are you sure? This property will be deleted"
|
||||
},
|
||||
"row": {
|
||||
"duplicate": "Duplicate",
|
||||
|
@ -287,8 +287,13 @@ class _BoardContentState extends State<BoardContent> {
|
||||
);
|
||||
}
|
||||
|
||||
void _openCard(String gridId, GridFieldController fieldController,
|
||||
RowPB rowPB, GridRowCache rowCache, BuildContext context) {
|
||||
void _openCard(
|
||||
String gridId,
|
||||
GridFieldController fieldController,
|
||||
RowPB rowPB,
|
||||
GridRowCache rowCache,
|
||||
BuildContext context,
|
||||
) {
|
||||
final rowInfo = RowInfo(
|
||||
gridId: gridId,
|
||||
fields: UnmodifiableListView(fieldController.fieldContexts),
|
||||
|
@ -2,6 +2,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'field_service.dart';
|
||||
import 'type_option/type_option_context.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@ -15,10 +16,11 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
FieldEditorBloc({
|
||||
required String gridId,
|
||||
required String fieldName,
|
||||
required bool isGroupField,
|
||||
required IFieldTypeOptionLoader loader,
|
||||
}) : dataController =
|
||||
TypeOptionDataController(gridId: gridId, loader: loader),
|
||||
super(FieldEditorState.initial(gridId, fieldName)) {
|
||||
super(FieldEditorState.initial(gridId, fieldName, isGroupField)) {
|
||||
on<FieldEditorEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
@ -35,7 +37,23 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
emit(state.copyWith(name: name));
|
||||
},
|
||||
didReceiveFieldChanged: (FieldPB field) {
|
||||
emit(state.copyWith(field: Some(field), name: field.name));
|
||||
emit(state.copyWith(
|
||||
field: Some(field),
|
||||
name: field.name,
|
||||
canDelete: field.isPrimary,
|
||||
));
|
||||
},
|
||||
deleteField: () {
|
||||
state.field.fold(
|
||||
() => null,
|
||||
(field) {
|
||||
final fieldService = FieldService(
|
||||
gridId: gridId,
|
||||
fieldId: field.id,
|
||||
);
|
||||
fieldService.deleteField();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -52,6 +70,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
class FieldEditorEvent with _$FieldEditorEvent {
|
||||
const factory FieldEditorEvent.initial() = _InitialField;
|
||||
const factory FieldEditorEvent.updateName(String name) = _UpdateName;
|
||||
const factory FieldEditorEvent.deleteField() = _DeleteField;
|
||||
const factory FieldEditorEvent.didReceiveFieldChanged(FieldPB field) =
|
||||
_DidReceiveFieldChanged;
|
||||
}
|
||||
@ -63,16 +82,21 @@ class FieldEditorState with _$FieldEditorState {
|
||||
required String errorText,
|
||||
required String name,
|
||||
required Option<FieldPB> field,
|
||||
required bool canDelete,
|
||||
required bool isGroupField,
|
||||
}) = _FieldEditorState;
|
||||
|
||||
factory FieldEditorState.initial(
|
||||
String gridId,
|
||||
String fieldName,
|
||||
bool isGroupField,
|
||||
) =>
|
||||
FieldEditorState(
|
||||
gridId: gridId,
|
||||
errorText: '',
|
||||
field: none(),
|
||||
canDelete: false,
|
||||
name: fieldName,
|
||||
isGroupField: isGroupField,
|
||||
);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/type_option
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_editor.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/prelude.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
@ -215,9 +216,15 @@ extension _FieldActionExtension on FieldAction {
|
||||
.add(const FieldActionSheetEvent.duplicateField());
|
||||
break;
|
||||
case FieldAction.delete:
|
||||
FlowyAlertDialog(
|
||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||
confirm: () {
|
||||
context
|
||||
.read<FieldActionSheetBloc>()
|
||||
.add(const FieldActionSheetEvent.deleteField());
|
||||
},
|
||||
).show(context);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,13 @@ 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:appflowy_popover/popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.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/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -13,6 +20,7 @@ import 'field_type_option_editor.dart';
|
||||
class FieldEditor extends StatefulWidget {
|
||||
final String gridId;
|
||||
final String fieldName;
|
||||
final bool isGroupField;
|
||||
final VoidCallback? onRemoved;
|
||||
|
||||
final IFieldTypeOptionLoader typeOptionLoader;
|
||||
@ -20,6 +28,7 @@ class FieldEditor extends StatefulWidget {
|
||||
required this.gridId,
|
||||
this.fieldName = "",
|
||||
required this.typeOptionLoader,
|
||||
this.isGroupField = false,
|
||||
this.onRemoved,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -41,9 +50,10 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => FieldEditorBloc(
|
||||
gridId: widget.gridId,
|
||||
fieldName: widget.fieldName,
|
||||
loader: widget.typeOptionLoader,
|
||||
gridId: gridId,
|
||||
fieldName: fieldName,
|
||||
isGroupField: isGroupField,
|
||||
loader: typeOptionLoader,
|
||||
)..add(const FieldEditorEvent.initial()),
|
||||
child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
|
||||
buildWhen: (p, c) => false,
|
||||
@ -56,6 +66,8 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
const VSpace(10),
|
||||
const _FieldNameCell(),
|
||||
const VSpace(10),
|
||||
const _DeleteFieldButton(),
|
||||
const VSpace(10),
|
||||
_FieldTypeOptionCell(popoverMutex: popoverMutex),
|
||||
],
|
||||
);
|
||||
@ -114,3 +126,47 @@ class _FieldNameCell extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeleteFieldButton extends StatelessWidget {
|
||||
const _DeleteFieldButton({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
|
||||
builder: (context, state) {
|
||||
final enable = !state.canDelete && !state.isGroupField;
|
||||
return SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.grid_field_delete.tr(),
|
||||
fontSize: 12,
|
||||
color: enable ? null : theme.shader4,
|
||||
),
|
||||
hoverColor: theme.hover,
|
||||
onTap: () {
|
||||
if (enable) {
|
||||
FlowyAlertDialog(
|
||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||
cancel: () {
|
||||
FlowyOverlay.of(context).remove(FieldEditor.identifier());
|
||||
},
|
||||
confirm: () {
|
||||
context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(const FieldEditorEvent.deleteField());
|
||||
FlowyOverlay.of(context).remove(FieldEditor.identifier());
|
||||
},
|
||||
).show(context);
|
||||
}
|
||||
},
|
||||
leftIcon: svgWidget('grid/delete', color: theme.iconColor),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void so() {}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_action_sheet_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
@ -150,9 +151,15 @@ extension _RowActionExtension on _RowAction {
|
||||
.add(const RowActionSheetEvent.duplicateRow());
|
||||
break;
|
||||
case _RowAction.delete:
|
||||
FlowyAlertDialog(
|
||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||
confirm: () {
|
||||
context
|
||||
.read<RowActionSheetBloc>()
|
||||
.add(const RowActionSheetEvent.deleteRow());
|
||||
},
|
||||
).show(context);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +133,7 @@ class _PropertyList extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const VSpace(10),
|
||||
_CreateFieldButton(
|
||||
viewId: viewId,
|
||||
onClosed: () {
|
||||
@ -180,8 +181,9 @@ class _CreateFieldButton extends StatelessWidget {
|
||||
triggerActions: PopoverTriggerActionFlags.click,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
onClose: onClosed,
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
height: 40,
|
||||
decoration: _makeBoxDecoration(context),
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.grid_field_newColumn.tr(),
|
||||
@ -195,6 +197,15 @@ class _CreateFieldButton extends StatelessWidget {
|
||||
popupBuilder: (BuildContext context) => onOpened(),
|
||||
);
|
||||
}
|
||||
|
||||
BoxDecoration _makeBoxDecoration(BuildContext context) {
|
||||
final theme = context.read<AppTheme>();
|
||||
final borderSide = BorderSide(color: theme.shader6, width: 1.0);
|
||||
return BoxDecoration(
|
||||
color: theme.surface,
|
||||
border: Border(top: borderSide),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RowDetailCell extends StatefulWidget {
|
||||
@ -247,6 +258,7 @@ class _RowDetailCellState extends State<_RowDetailCell> {
|
||||
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,
|
||||
|
@ -56,7 +56,8 @@ class _CreateTextFieldDialog extends State<TextFieldDialog> {
|
||||
FlowyFormTextInput(
|
||||
hintText: LocaleKeys.dialogCreatePageNameHint.tr(),
|
||||
initialValue: widget.value,
|
||||
textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
|
||||
textStyle:
|
||||
const TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
|
||||
autoFocus: true,
|
||||
onChanged: (text) {
|
||||
newValue = text;
|
||||
@ -120,7 +121,7 @@ class _CreateFlowyAlertDialog extends State<FlowyAlertDialog> {
|
||||
const VSpace(20),
|
||||
OkCancelButton(
|
||||
onOkPressed: widget.confirm!,
|
||||
onCancelPressed: widget.confirm,
|
||||
onCancelPressed: widget.cancel,
|
||||
)
|
||||
]
|
||||
],
|
||||
@ -158,7 +159,7 @@ class OkCancelDialog extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (title != null) ...[
|
||||
Text(title!.toUpperCase(), style: TextStyles.T1.textColor(theme.shader1)),
|
||||
FlowyText.medium(title!.toUpperCase(), color: theme.shader1),
|
||||
VSpace(Insets.sm * 1.5),
|
||||
Container(color: theme.bg1, height: 1),
|
||||
VSpace(Insets.m * 1.5),
|
||||
@ -185,7 +186,12 @@ class OkCancelButton extends StatelessWidget {
|
||||
final double? minHeight;
|
||||
|
||||
const OkCancelButton(
|
||||
{Key? key, this.onOkPressed, this.onCancelPressed, this.okTitle, this.cancelTitle, this.minHeight})
|
||||
{Key? key,
|
||||
this.onOkPressed,
|
||||
this.onCancelPressed,
|
||||
this.okTitle,
|
||||
this.cancelTitle,
|
||||
this.minHeight})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -200,7 +206,7 @@ class OkCancelButton extends StatelessWidget {
|
||||
cancelTitle ?? LocaleKeys.button_Cancel.tr(),
|
||||
onPressed: () {
|
||||
onCancelPressed!();
|
||||
AppGlobals.nav.pop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
bigMode: true,
|
||||
),
|
||||
@ -210,7 +216,7 @@ class OkCancelButton extends StatelessWidget {
|
||||
okTitle ?? LocaleKeys.button_OK.tr(),
|
||||
onPressed: () {
|
||||
onOkPressed!();
|
||||
AppGlobals.nav.pop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
bigMode: true,
|
||||
),
|
||||
|
@ -1,22 +1,30 @@
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/text_style.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'base_styled_button.dart';
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
|
||||
class PrimaryTextButton extends StatelessWidget {
|
||||
final String label;
|
||||
final VoidCallback? onPressed;
|
||||
final bool bigMode;
|
||||
|
||||
const PrimaryTextButton(this.label, {Key? key, this.onPressed, this.bigMode = false}) : super(key: key);
|
||||
const PrimaryTextButton(this.label,
|
||||
{Key? key, this.onPressed, this.bigMode = false})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextStyle txtStyle = TextStyles.Btn.textColor(Colors.white);
|
||||
return PrimaryButton(bigMode: bigMode, onPressed: onPressed, child: Text(label, style: txtStyle));
|
||||
final theme = context.watch<AppTheme>();
|
||||
return PrimaryButton(
|
||||
bigMode: bigMode,
|
||||
onPressed: onPressed,
|
||||
child: FlowyText.regular(
|
||||
label,
|
||||
color: theme.surface,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,14 +33,16 @@ class PrimaryButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final bool bigMode;
|
||||
|
||||
const PrimaryButton({Key? key, required this.child, this.onPressed, this.bigMode = false}) : super(key: key);
|
||||
const PrimaryButton(
|
||||
{Key? key, required this.child, this.onPressed, this.bigMode = false})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return BaseStyledButton(
|
||||
minWidth: bigMode ? 170 : 78,
|
||||
minHeight: bigMode ? 48 : 28,
|
||||
minWidth: bigMode ? 100 : 80,
|
||||
minHeight: bigMode ? 40 : 38,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
bgColor: theme.main1,
|
||||
hoverColor: theme.main1,
|
||||
|
@ -1,9 +1,8 @@
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
// ignore: import_of_legacy_library_into_null_safe
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/text_style.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'base_styled_button.dart';
|
||||
|
||||
@ -12,13 +11,21 @@ class SecondaryTextButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final bool bigMode;
|
||||
|
||||
const SecondaryTextButton(this.label, {Key? key, this.onPressed, this.bigMode = false}) : super(key: key);
|
||||
const SecondaryTextButton(this.label,
|
||||
{Key? key, this.onPressed, this.bigMode = false})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
TextStyle txtStyle = TextStyles.Btn.textColor(theme.main1);
|
||||
return SecondaryButton(bigMode: bigMode, onPressed: onPressed, child: Text(label, style: txtStyle));
|
||||
return SecondaryButton(
|
||||
bigMode: bigMode,
|
||||
onPressed: onPressed,
|
||||
child: FlowyText.regular(
|
||||
label,
|
||||
color: theme.main1,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,14 +34,16 @@ class SecondaryButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final bool bigMode;
|
||||
|
||||
const SecondaryButton({Key? key, required this.child, this.onPressed, this.bigMode = false}) : super(key: key);
|
||||
const SecondaryButton(
|
||||
{Key? key, required this.child, this.onPressed, this.bigMode = false})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return BaseStyledButton(
|
||||
minWidth: bigMode ? 170 : 78,
|
||||
minHeight: bigMode ? 48 : 28,
|
||||
minWidth: bigMode ? 100 : 80,
|
||||
minHeight: bigMode ? 40 : 38,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
bgColor: theme.shader7,
|
||||
hoverColor: theme.hover,
|
||||
|
@ -1,3 +1,3 @@
|
||||
class DialogSize {
|
||||
static double get minDialogWidth => 480;
|
||||
static double get minDialogWidth => 400;
|
||||
}
|
||||
|
@ -133,7 +133,9 @@ class StyledDialogRoute<T> extends PopupRoute<T> {
|
||||
super(settings: settings, filter: barrier.filter);
|
||||
|
||||
@override
|
||||
bool get barrierDismissible => barrier.dismissible;
|
||||
bool get barrierDismissible {
|
||||
return barrier.dismissible;
|
||||
}
|
||||
|
||||
@override
|
||||
String get barrierLabel => barrier.label;
|
||||
|
Loading…
x
Reference in New Issue
Block a user