mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: grid property list
This commit is contained in:
parent
5d007c5359
commit
6a94594a35
@ -165,8 +165,8 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<GridFieldBloc, GridFieldData, void>(
|
||||
(data, _) => GridFieldBloc(
|
||||
getIt.registerFactoryParam<FieldActionSheetBloc, GridFieldCellContext, void>(
|
||||
(data, _) => FieldActionSheetBloc(
|
||||
field: data.field,
|
||||
service: FieldService(gridId: data.gridId),
|
||||
),
|
||||
@ -214,8 +214,8 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<FieldSwitchBloc, SwitchFieldContext, void>(
|
||||
(context, _) => FieldSwitchBloc(context),
|
||||
getIt.registerFactoryParam<FieldSwitcherBloc, SwitchFieldContext, void>(
|
||||
(context, _) => FieldSwitcherBloc(context),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<SingleSelectTypeOptionBloc, SingleSelectTypeOption, String>(
|
||||
@ -233,4 +233,8 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
getIt.registerFactoryParam<NumberTypeOptionBloc, NumberTypeOption, void>(
|
||||
(typeOption, _) => NumberTypeOptionBloc(typeOption: typeOption),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<GridPropertyBloc, String, List<Field>>(
|
||||
(gridId, fields) => GridPropertyBloc(gridId: gridId, fields: fields),
|
||||
);
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'field_service.dart';
|
||||
|
||||
part 'grid_field_bloc.freezed.dart';
|
||||
part 'action_sheet_bloc.freezed.dart';
|
||||
|
||||
class GridFieldBloc extends Bloc<GridFieldEvent, GridFieldState> {
|
||||
class FieldActionSheetBloc extends Bloc<ActionSheetEvent, ActionSheetState> {
|
||||
final FieldService service;
|
||||
|
||||
GridFieldBloc({required Field field, required this.service})
|
||||
: super(GridFieldState.initial(EditFieldContext.create()..gridField = field)) {
|
||||
on<GridFieldEvent>(
|
||||
FieldActionSheetBloc({required Field field, required this.service})
|
||||
: super(ActionSheetState.initial(EditFieldContext.create()..gridField = field)) {
|
||||
on<ActionSheetEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
updateFieldName: (_UpdateFieldName value) async {
|
||||
@ -56,23 +56,23 @@ class GridFieldBloc extends Bloc<GridFieldEvent, GridFieldState> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridFieldEvent with _$GridFieldEvent {
|
||||
const factory GridFieldEvent.updateFieldName(String name) = _UpdateFieldName;
|
||||
const factory GridFieldEvent.hideField() = _HideField;
|
||||
const factory GridFieldEvent.duplicateField() = _DuplicateField;
|
||||
const factory GridFieldEvent.deleteField() = _DeleteField;
|
||||
const factory GridFieldEvent.saveField() = _SaveField;
|
||||
class ActionSheetEvent with _$ActionSheetEvent {
|
||||
const factory ActionSheetEvent.updateFieldName(String name) = _UpdateFieldName;
|
||||
const factory ActionSheetEvent.hideField() = _HideField;
|
||||
const factory ActionSheetEvent.duplicateField() = _DuplicateField;
|
||||
const factory ActionSheetEvent.deleteField() = _DeleteField;
|
||||
const factory ActionSheetEvent.saveField() = _SaveField;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridFieldState with _$GridFieldState {
|
||||
const factory GridFieldState({
|
||||
class ActionSheetState with _$ActionSheetState {
|
||||
const factory ActionSheetState({
|
||||
required EditFieldContext editContext,
|
||||
required String errorText,
|
||||
required String fieldName,
|
||||
}) = _GridFieldState;
|
||||
}) = _ActionSheetState;
|
||||
|
||||
factory GridFieldState.initial(EditFieldContext editContext) => GridFieldState(
|
||||
factory ActionSheetState.initial(EditFieldContext editContext) => ActionSheetState(
|
||||
editContext: editContext,
|
||||
errorText: '',
|
||||
fieldName: editContext.gridField.name,
|
@ -7,7 +7,7 @@ import 'dart:async';
|
||||
import 'field_service.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
part 'edit_field_bloc.freezed.dart';
|
||||
part 'field_editor_bloc.freezed.dart';
|
||||
|
||||
class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
final FieldService service;
|
@ -98,11 +98,11 @@ class FieldService {
|
||||
}
|
||||
}
|
||||
|
||||
class GridFieldData extends Equatable {
|
||||
class GridFieldCellContext extends Equatable {
|
||||
final String gridId;
|
||||
final Field field;
|
||||
|
||||
const GridFieldData({
|
||||
const GridFieldCellContext({
|
||||
required this.gridId,
|
||||
required this.field,
|
||||
});
|
||||
@ -132,7 +132,7 @@ class NewFieldContextLoader extends FieldContextLoader {
|
||||
}
|
||||
|
||||
class FieldContextLoaderAdaptor extends FieldContextLoader {
|
||||
final GridFieldData data;
|
||||
final GridFieldCellContext data;
|
||||
|
||||
FieldContextLoaderAdaptor(this.data);
|
||||
|
||||
|
@ -7,10 +7,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'field_service.dart';
|
||||
|
||||
part 'switch_field_type_bloc.freezed.dart';
|
||||
part 'field_switch_bloc.freezed.dart';
|
||||
|
||||
class FieldSwitchBloc extends Bloc<FieldSwitchEvent, FieldSwitchState> {
|
||||
FieldSwitchBloc(SwitchFieldContext editContext) : super(FieldSwitchState.initial(editContext)) {
|
||||
class FieldSwitcherBloc extends Bloc<FieldSwitchEvent, FieldSwitchState> {
|
||||
FieldSwitcherBloc(SwitchFieldContext editContext) : super(FieldSwitchState.initial(editContext)) {
|
||||
on<FieldSwitchEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
@ -27,6 +27,7 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
||||
createField: (_CreateField value) {},
|
||||
insertField: (_InsertField value) {},
|
||||
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||
value.fields.retainWhere((field) => field.visibility);
|
||||
emit(state.copyWith(fields: value.fields));
|
||||
},
|
||||
);
|
||||
@ -64,5 +65,8 @@ class GridHeaderEvent with _$GridHeaderEvent {
|
||||
class GridHeaderState with _$GridHeaderState {
|
||||
const factory GridHeaderState({required List<Field> fields}) = _GridHeaderState;
|
||||
|
||||
factory GridHeaderState.initial(List<Field> fields) => GridHeaderState(fields: fields);
|
||||
factory GridHeaderState.initial(List<Field> fields) {
|
||||
fields.retainWhere((field) => field.visibility);
|
||||
return GridHeaderState(fields: fields);
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ export 'data.dart';
|
||||
// Field
|
||||
export 'field/field_service.dart';
|
||||
export 'field/grid_header_bloc.dart';
|
||||
export 'field/grid_field_bloc.dart';
|
||||
export 'field/edit_field_bloc.dart';
|
||||
export 'field/switch_field_type_bloc.dart';
|
||||
export 'field/action_sheet_bloc.dart';
|
||||
export 'field/field_editor_bloc.dart';
|
||||
export 'field/field_switch_bloc.dart';
|
||||
|
||||
// Field Type Option
|
||||
export 'field/type_option/date_bloc.dart';
|
||||
@ -26,3 +26,4 @@ export 'cell_bloc/cell_service.dart';
|
||||
|
||||
// Setting
|
||||
export 'setting/setting_bloc.dart';
|
||||
export 'setting/property_bloc.dart';
|
||||
|
@ -0,0 +1,51 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
part 'property_bloc.freezed.dart';
|
||||
|
||||
class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
|
||||
final FieldService _service;
|
||||
GridPropertyBloc({required String gridId, required List<Field> fields})
|
||||
: _service = FieldService(gridId: gridId),
|
||||
super(GridPropertyState.initial(gridId, fields)) {
|
||||
on<GridPropertyEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(setFieldVisibility: (_SetFieldVisibility value) async {
|
||||
final result = await _service.updateField(fieldId: value.fieldId, visibility: value.visibility);
|
||||
result.fold(
|
||||
(l) => null,
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridPropertyEvent with _$GridPropertyEvent {
|
||||
const factory GridPropertyEvent.setFieldVisibility(String fieldId, bool visibility) = _SetFieldVisibility;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridPropertyState with _$GridPropertyState {
|
||||
const factory GridPropertyState({
|
||||
required String gridId,
|
||||
required List<Field> fields,
|
||||
}) = _GridPropertyState;
|
||||
|
||||
factory GridPropertyState.initial(String gridId, List<Field> fields) => GridPropertyState(
|
||||
gridId: gridId,
|
||||
fields: fields,
|
||||
);
|
||||
}
|
@ -100,9 +100,9 @@ class _FlowyGridState extends State<FlowyGrid> {
|
||||
physics: StyledScrollPhysics(),
|
||||
controller: _scrollController.verticalController,
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: GridToolbar(gridId: gridId)),
|
||||
_buildHeader(gridId),
|
||||
_buildRows(context),
|
||||
_renderToolbar(gridId),
|
||||
_renderHeader(gridId),
|
||||
_renderRows(context),
|
||||
const GridFooter(),
|
||||
],
|
||||
),
|
||||
@ -129,12 +129,27 @@ class _FlowyGridState extends State<FlowyGrid> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(String gridId) {
|
||||
Widget _renderToolbar(String gridId) {
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
builder: (context, state) {
|
||||
final toolbarContext = GridToolbarContext(
|
||||
gridId: gridId,
|
||||
fields: state.fields,
|
||||
);
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: GridToolbar(toolbarContext: toolbarContext),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderHeader(String gridId) {
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
|
||||
builder: (context, state) {
|
||||
return SliverPersistentHeader(
|
||||
delegate: GridHeaderDelegate(gridId: gridId, fields: state.fields),
|
||||
delegate: GridHeaderDelegate(gridId: gridId, fields: List.from(state.fields)),
|
||||
floating: true,
|
||||
pinned: true,
|
||||
);
|
||||
@ -142,7 +157,7 @@ class _FlowyGridState extends State<FlowyGrid> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRows(BuildContext context) {
|
||||
Widget _renderRows(BuildContext context) {
|
||||
return BlocBuilder<GridBloc, GridState>(
|
||||
buildWhen: (previous, current) {
|
||||
final rowChanged = previous.rows.length != current.rows.length;
|
||||
|
@ -124,6 +124,7 @@ class _RowCells extends StatelessWidget {
|
||||
buildWhen: (previous, current) => previous.cellDataMap != current.cellDataMap,
|
||||
builder: (context, state) {
|
||||
final children = state.fields
|
||||
.where((field) => field.visibility)
|
||||
.map((field) => CellContainer(
|
||||
width: field.width.toDouble(),
|
||||
child: buildGridCell(
|
||||
|
@ -0,0 +1,55 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'field_type_extension.dart';
|
||||
|
||||
import 'field_cell_action_sheet.dart';
|
||||
import 'field_editor.dart';
|
||||
|
||||
class GridFieldCell extends StatelessWidget {
|
||||
final GridFieldCellContext fieldCellContext;
|
||||
const GridFieldCell(this.fieldCellContext, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final field = fieldCellContext.field;
|
||||
|
||||
final button = FlowyButton(
|
||||
hoverColor: theme.hover,
|
||||
onTap: () => _showActionSheet(context),
|
||||
rightIcon: svgWidget("editor/details", color: theme.iconColor),
|
||||
leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
|
||||
text: FlowyText.medium(field.name, fontSize: 12),
|
||||
padding: GridSize.cellContentInsets,
|
||||
);
|
||||
|
||||
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
|
||||
final decoration = BoxDecoration(border: Border(top: borderSide, right: borderSide, bottom: borderSide));
|
||||
|
||||
return Container(
|
||||
width: field.width.toDouble(),
|
||||
decoration: decoration,
|
||||
child: button,
|
||||
);
|
||||
}
|
||||
|
||||
void _showActionSheet(BuildContext context) {
|
||||
GridFieldCellActionSheet(
|
||||
fieldCellContext: fieldCellContext,
|
||||
onEdited: () => _showFieldEditor(context),
|
||||
).show(context);
|
||||
}
|
||||
|
||||
void _showFieldEditor(BuildContext context) {
|
||||
FieldEditor(
|
||||
gridId: fieldCellContext.gridId,
|
||||
fieldContextLoader: FieldContextLoaderAdaptor(fieldCellContext),
|
||||
).show(context);
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.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';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
|
||||
class GridFieldCellActionSheet extends StatelessWidget with FlowyOverlayDelegate {
|
||||
final GridFieldCellContext fieldCellContext;
|
||||
final VoidCallback onEdited;
|
||||
const GridFieldCellActionSheet({required this.fieldCellContext, required this.onEdited, Key? key}) : super(key: key);
|
||||
|
||||
void show(BuildContext overlayContext) {
|
||||
FlowyOverlay.of(overlayContext).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: this,
|
||||
constraints: BoxConstraints.loose(const Size(240, 200)),
|
||||
),
|
||||
identifier: identifier(),
|
||||
anchorContext: overlayContext,
|
||||
anchorDirection: AnchorDirection.bottomWithLeftAligned,
|
||||
delegate: this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<FieldActionSheetBloc>(param1: fieldCellContext),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_EditFieldButton(
|
||||
onEdited: () {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
onEdited();
|
||||
},
|
||||
),
|
||||
const VSpace(6),
|
||||
_FieldOperationList(fieldCellContext, () => FlowyOverlay.of(context).remove(identifier())),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String identifier() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool asBarrier() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class _EditFieldButton extends StatelessWidget {
|
||||
final Function() onEdited;
|
||||
const _EditFieldButton({required this.onEdited, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return BlocBuilder<FieldActionSheetBloc, ActionSheetState>(
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
onTap: onEdited,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FieldOperationList extends StatelessWidget {
|
||||
final GridFieldCellContext fieldData;
|
||||
final VoidCallback onDismissed;
|
||||
const _FieldOperationList(this.fieldData, this.onDismissed, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final actions = FieldAction.values
|
||||
.map(
|
||||
(action) => FieldActionCell(
|
||||
fieldId: fieldData.field.id,
|
||||
action: action,
|
||||
onTap: onDismissed,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return FieldOperationList(actions: actions);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldOperationList extends StatelessWidget {
|
||||
final List<FieldActionCell> actions;
|
||||
const FieldOperationList({required this.actions, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView(
|
||||
// https://api.flutter.dev/flutter/widgets/AnimatedList/shrinkWrap.html
|
||||
shrinkWrap: true,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 4.0,
|
||||
mainAxisSpacing: 8,
|
||||
),
|
||||
children: actions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldActionCell extends StatelessWidget {
|
||||
final String fieldId;
|
||||
final VoidCallback onTap;
|
||||
final FieldAction action;
|
||||
|
||||
const FieldActionCell({
|
||||
required this.fieldId,
|
||||
required this.action,
|
||||
required this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return FlowyButton(
|
||||
text: FlowyText.medium(action.title(), fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
onTap: () {
|
||||
action.run(context);
|
||||
onTap();
|
||||
},
|
||||
leftIcon: svgWidget(action.iconName(), color: theme.iconColor),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum FieldAction {
|
||||
hide,
|
||||
duplicate,
|
||||
delete,
|
||||
}
|
||||
|
||||
extension _FieldActionExtension on FieldAction {
|
||||
String iconName() {
|
||||
switch (this) {
|
||||
case FieldAction.hide:
|
||||
return 'grid/hide';
|
||||
case FieldAction.duplicate:
|
||||
return 'grid/duplicate';
|
||||
case FieldAction.delete:
|
||||
return 'grid/delete';
|
||||
}
|
||||
}
|
||||
|
||||
String title() {
|
||||
switch (this) {
|
||||
case FieldAction.hide:
|
||||
return LocaleKeys.grid_field_hide.tr();
|
||||
case FieldAction.duplicate:
|
||||
return LocaleKeys.grid_field_duplicate.tr();
|
||||
case FieldAction.delete:
|
||||
return LocaleKeys.grid_field_delete.tr();
|
||||
}
|
||||
}
|
||||
|
||||
void run(BuildContext context) {
|
||||
switch (this) {
|
||||
case FieldAction.hide:
|
||||
context.read<FieldActionSheetBloc>().add(const ActionSheetEvent.hideField());
|
||||
break;
|
||||
case FieldAction.duplicate:
|
||||
context.read<FieldActionSheetBloc>().add(const ActionSheetEvent.duplicateField());
|
||||
break;
|
||||
case FieldAction.delete:
|
||||
context.read<FieldActionSheetBloc>().add(const ActionSheetEvent.deleteField());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/edit_field_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_editor_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/switch_field_type_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_switch_bloc.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
@ -22,7 +22,11 @@ class FieldEditor extends FlowyOverlayDelegate {
|
||||
_fieldEditorBloc.add(const FieldEditorEvent.initial());
|
||||
}
|
||||
|
||||
void show(BuildContext context) {
|
||||
void show(
|
||||
BuildContext context, {
|
||||
AnchorDirection anchorDirection = AnchorDirection.bottomWithLeftAligned,
|
||||
}) {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: _FieldEditorWidget(_fieldEditorBloc),
|
||||
@ -30,7 +34,7 @@ class FieldEditor extends FlowyOverlayDelegate {
|
||||
),
|
||||
identifier: identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.bottomWithLeftAligned,
|
||||
anchorDirection: anchorDirection,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
delegate: this,
|
||||
);
|
@ -1,99 +0,0 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/grid_field_bloc.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class FieldOperationList extends StatelessWidget {
|
||||
final List<FieldActionCell> actions;
|
||||
const FieldOperationList({required this.actions, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView(
|
||||
// https://api.flutter.dev/flutter/widgets/AnimatedList/shrinkWrap.html
|
||||
shrinkWrap: true,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 4.0,
|
||||
mainAxisSpacing: 8,
|
||||
),
|
||||
children: actions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldActionCell extends StatelessWidget {
|
||||
final String fieldId;
|
||||
final VoidCallback onTap;
|
||||
final FieldAction action;
|
||||
|
||||
const FieldActionCell({
|
||||
required this.fieldId,
|
||||
required this.action,
|
||||
required this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return FlowyButton(
|
||||
text: FlowyText.medium(action.title(), fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
onTap: () {
|
||||
action.run(context);
|
||||
onTap();
|
||||
},
|
||||
leftIcon: svgWidget(action.iconName(), color: theme.iconColor),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum FieldAction {
|
||||
hide,
|
||||
duplicate,
|
||||
delete,
|
||||
}
|
||||
|
||||
extension _FieldActionExtension on FieldAction {
|
||||
String iconName() {
|
||||
switch (this) {
|
||||
case FieldAction.hide:
|
||||
return 'grid/hide';
|
||||
case FieldAction.duplicate:
|
||||
return 'grid/duplicate';
|
||||
case FieldAction.delete:
|
||||
return 'grid/delete';
|
||||
}
|
||||
}
|
||||
|
||||
String title() {
|
||||
switch (this) {
|
||||
case FieldAction.hide:
|
||||
return LocaleKeys.grid_field_hide.tr();
|
||||
case FieldAction.duplicate:
|
||||
return LocaleKeys.grid_field_duplicate.tr();
|
||||
case FieldAction.delete:
|
||||
return LocaleKeys.grid_field_delete.tr();
|
||||
}
|
||||
}
|
||||
|
||||
void run(BuildContext context) {
|
||||
switch (this) {
|
||||
case FieldAction.hide:
|
||||
context.read<GridFieldBloc>().add(const GridFieldEvent.hideField());
|
||||
break;
|
||||
case FieldAction.duplicate:
|
||||
context.read<GridFieldBloc>().add(const GridFieldEvent.duplicateField());
|
||||
break;
|
||||
case FieldAction.delete:
|
||||
context.read<GridFieldBloc>().add(const GridFieldEvent.deleteField());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_list.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart';
|
||||
import 'field_type_extension.dart';
|
||||
|
||||
import 'type_option/multi_select.dart';
|
||||
import 'type_option/number.dart';
|
||||
@ -43,8 +44,8 @@ class _FieldSwitcherState extends State<FieldSwitcher> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<FieldSwitchBloc>(param1: widget.switchContext),
|
||||
child: BlocConsumer<FieldSwitchBloc, FieldSwitchState>(
|
||||
create: (context) => getIt<FieldSwitcherBloc>(param1: widget.switchContext),
|
||||
child: BlocConsumer<FieldSwitcherBloc, FieldSwitchState>(
|
||||
listener: (context, state) {
|
||||
widget.onUpdated(state.field, state.typeOptionData);
|
||||
},
|
||||
@ -79,7 +80,7 @@ class _FieldSwitcherState extends State<FieldSwitcher> {
|
||||
hoverColor: theme.hover,
|
||||
onTap: () {
|
||||
final list = FieldTypeList(onSelectField: (fieldType) {
|
||||
context.read<FieldSwitchBloc>().add(FieldSwitchEvent.toFieldType(fieldType));
|
||||
context.read<FieldSwitcherBloc>().add(FieldSwitchEvent.toFieldType(fieldType));
|
||||
});
|
||||
_showOverlay(context, list);
|
||||
},
|
||||
@ -100,7 +101,7 @@ class _FieldSwitcherState extends State<FieldSwitcher> {
|
||||
);
|
||||
|
||||
final dataDelegate = TypeOptionDataDelegate(didUpdateTypeOptionData: (data) {
|
||||
context.read<FieldSwitchBloc>().add(FieldSwitchEvent.didUpdateTypeOptionData(data));
|
||||
context.read<FieldSwitcherBloc>().add(FieldSwitchEvent.didUpdateTypeOptionData(data));
|
||||
});
|
||||
|
||||
final builder = _makeTypeOptionBuild(
|
||||
|
@ -0,0 +1,44 @@
|
||||
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
extension FieldTypeListExtension on FieldType {
|
||||
String iconName() {
|
||||
switch (this) {
|
||||
case FieldType.Checkbox:
|
||||
return "grid/field/checkbox";
|
||||
case FieldType.DateTime:
|
||||
return "grid/field/date";
|
||||
case FieldType.MultiSelect:
|
||||
return "grid/field/multi_select";
|
||||
case FieldType.Number:
|
||||
return "grid/field/number";
|
||||
case FieldType.RichText:
|
||||
return "grid/field/text";
|
||||
case FieldType.SingleSelect:
|
||||
return "grid/field/single_select";
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
|
||||
String title() {
|
||||
switch (this) {
|
||||
case FieldType.Checkbox:
|
||||
return LocaleKeys.grid_field_checkboxFieldName.tr();
|
||||
case FieldType.DateTime:
|
||||
return LocaleKeys.grid_field_dateFieldName.tr();
|
||||
case FieldType.MultiSelect:
|
||||
return LocaleKeys.grid_field_multiSelectFieldName.tr();
|
||||
case FieldType.Number:
|
||||
return LocaleKeys.grid_field_numberFieldName.tr();
|
||||
case FieldType.RichText:
|
||||
return LocaleKeys.grid_field_textFieldName.tr();
|
||||
case FieldType.SingleSelect:
|
||||
return LocaleKeys.grid_field_singleSelectFieldName.tr();
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,8 +8,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'field_type_extension.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
typedef SelectFieldCallback = void Function(FieldType);
|
||||
@ -76,43 +75,3 @@ class FieldTypeCell extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension FieldTypeListExtension on FieldType {
|
||||
String iconName() {
|
||||
switch (this) {
|
||||
case FieldType.Checkbox:
|
||||
return "grid/field/checkbox";
|
||||
case FieldType.DateTime:
|
||||
return "grid/field/date";
|
||||
case FieldType.MultiSelect:
|
||||
return "grid/field/multi_select";
|
||||
case FieldType.Number:
|
||||
return "grid/field/number";
|
||||
case FieldType.RichText:
|
||||
return "grid/field/text";
|
||||
case FieldType.SingleSelect:
|
||||
return "grid/field/single_select";
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
|
||||
String title() {
|
||||
switch (this) {
|
||||
case FieldType.Checkbox:
|
||||
return LocaleKeys.grid_field_checkboxFieldName.tr();
|
||||
case FieldType.DateTime:
|
||||
return LocaleKeys.grid_field_dateFieldName.tr();
|
||||
case FieldType.MultiSelect:
|
||||
return LocaleKeys.grid_field_multiSelectFieldName.tr();
|
||||
case FieldType.Number:
|
||||
return LocaleKeys.grid_field_numberFieldName.tr();
|
||||
case FieldType.RichText:
|
||||
return LocaleKeys.grid_field_textFieldName.tr();
|
||||
case FieldType.SingleSelect:
|
||||
return LocaleKeys.grid_field_singleSelectFieldName.tr();
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.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';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'field_operation_list.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
|
||||
class GridFieldActionSheet extends StatelessWidget with FlowyOverlayDelegate {
|
||||
final GridFieldData fieldData;
|
||||
final VoidCallback onEdited;
|
||||
const GridFieldActionSheet({required this.fieldData, required this.onEdited, Key? key}) : super(key: key);
|
||||
|
||||
void show(BuildContext overlayContext) {
|
||||
FlowyOverlay.of(overlayContext).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: this,
|
||||
constraints: BoxConstraints.loose(const Size(240, 200)),
|
||||
),
|
||||
identifier: identifier(),
|
||||
anchorContext: overlayContext,
|
||||
anchorDirection: AnchorDirection.bottomWithLeftAligned,
|
||||
delegate: this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<GridFieldBloc>(param1: fieldData),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_EditFieldButton(
|
||||
onEdited: () {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
onEdited();
|
||||
},
|
||||
),
|
||||
const VSpace(6),
|
||||
_FieldOperationList(fieldData, () => FlowyOverlay.of(context).remove(identifier())),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String identifier() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool asBarrier() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class _EditFieldButton extends StatelessWidget {
|
||||
final Function() onEdited;
|
||||
const _EditFieldButton({required this.onEdited, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return BlocBuilder<GridFieldBloc, GridFieldState>(
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
onTap: onEdited,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FieldOperationList extends StatelessWidget {
|
||||
final GridFieldData fieldData;
|
||||
final VoidCallback onDismissed;
|
||||
const _FieldOperationList(this.fieldData, this.onDismissed, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final actions = FieldAction.values
|
||||
.map(
|
||||
(action) => FieldActionCell(
|
||||
fieldId: fieldData.field.id,
|
||||
action: action,
|
||||
onTap: onDismissed,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return FieldOperationList(actions: actions);
|
||||
}
|
||||
}
|
@ -9,8 +9,8 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'grid_field_editor.dart';
|
||||
import 'grid_header_cell.dart';
|
||||
import 'field_editor.dart';
|
||||
import 'field_cell.dart';
|
||||
|
||||
class GridHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
final String gridId;
|
||||
@ -55,8 +55,8 @@ class GridHeader extends StatelessWidget {
|
||||
child: BlocBuilder<GridHeaderBloc, GridHeaderState>(
|
||||
builder: (context, state) {
|
||||
final cells = state.fields.map(
|
||||
(field) => GridHeaderCell(
|
||||
GridFieldData(gridId: gridId, field: field),
|
||||
(field) => GridFieldCell(
|
||||
GridFieldCellContext(gridId: gridId, field: field),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -1,45 +0,0 @@
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'grid_field_editor.dart';
|
||||
import 'grid_field_action_sheet.dart';
|
||||
|
||||
class GridHeaderCell extends StatelessWidget {
|
||||
final GridFieldData fieldData;
|
||||
const GridHeaderCell(this.fieldData, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final button = FlowyButton(
|
||||
hoverColor: theme.hover,
|
||||
onTap: () => GridFieldActionSheet(
|
||||
fieldData: fieldData,
|
||||
onEdited: () {
|
||||
FieldEditor(
|
||||
gridId: fieldData.gridId,
|
||||
fieldContextLoader: FieldContextLoaderAdaptor(fieldData),
|
||||
).show(context);
|
||||
},
|
||||
).show(context),
|
||||
rightIcon: svgWidget("editor/details", color: theme.iconColor),
|
||||
text: Padding(padding: GridSize.cellContentInsets, child: FlowyText.medium(fieldData.field.name, fontSize: 12)),
|
||||
);
|
||||
|
||||
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
|
||||
final decoration = BoxDecoration(border: Border(top: borderSide, right: borderSide, bottom: borderSide));
|
||||
|
||||
return Container(
|
||||
width: fieldData.field.width.toDouble(),
|
||||
decoration: decoration,
|
||||
padding: GridSize.headerContentInsets,
|
||||
child: button,
|
||||
);
|
||||
}
|
||||
}
|
@ -109,8 +109,8 @@ class DateFormatList extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formatItems = DateFormat.values.map((format) {
|
||||
return DateFormatItem(
|
||||
final cells = DateFormat.values.map((format) {
|
||||
return DateFormatCell(
|
||||
dateFormat: format,
|
||||
onSelected: (format) {
|
||||
onSelected(format);
|
||||
@ -127,9 +127,9 @@ class DateFormatList extends StatelessWidget {
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemCount: formatItems.length,
|
||||
itemCount: cells.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return formatItems[index];
|
||||
return cells[index];
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -140,11 +140,11 @@ class DateFormatList extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DateFormatItem extends StatelessWidget {
|
||||
class DateFormatCell extends StatelessWidget {
|
||||
final bool isSelected;
|
||||
final DateFormat dateFormat;
|
||||
final Function(DateFormat format) onSelected;
|
||||
const DateFormatItem({
|
||||
const DateFormatCell({
|
||||
required this.dateFormat,
|
||||
required this.onSelected,
|
||||
required this.isSelected,
|
||||
@ -199,8 +199,8 @@ class TimeFormatList extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formatItems = TimeFormat.values.map((format) {
|
||||
return TimeFormatItem(
|
||||
final cells = TimeFormat.values.map((format) {
|
||||
return TimeFormatCell(
|
||||
isSelected: format == selectedFormat,
|
||||
timeFormat: format,
|
||||
onSelected: (format) {
|
||||
@ -217,9 +217,9 @@ class TimeFormatList extends StatelessWidget {
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemCount: formatItems.length,
|
||||
itemCount: cells.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return formatItems[index];
|
||||
return cells[index];
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -230,11 +230,11 @@ class TimeFormatList extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class TimeFormatItem extends StatelessWidget {
|
||||
class TimeFormatCell extends StatelessWidget {
|
||||
final TimeFormat timeFormat;
|
||||
final bool isSelected;
|
||||
final Function(TimeFormat format) onSelected;
|
||||
const TimeFormatItem({
|
||||
const TimeFormatCell({
|
||||
required this.timeFormat,
|
||||
required this.onSelected,
|
||||
required this.isSelected,
|
||||
|
@ -1,19 +1,123 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/setting/property_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_extension.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/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
class GridPropertyList extends StatelessWidget {
|
||||
const GridPropertyList({Key? key}) : super(key: key);
|
||||
class GridPropertyList extends StatelessWidget with FlowyOverlayDelegate {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
const GridPropertyList({
|
||||
required this.gridId,
|
||||
required this.fields,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
void show(BuildContext context) {
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: this,
|
||||
constraints: BoxConstraints.loose(const Size(260, 400)),
|
||||
),
|
||||
identifier: identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.bottomRight,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
delegate: this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container();
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<GridPropertyBloc>(param1: gridId, param2: fields),
|
||||
child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
|
||||
builder: (context, state) {
|
||||
final cells = state.fields.map((field) {
|
||||
return _GridPropertyCell(gridId: gridId, field: field);
|
||||
}).toList();
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemCount: cells.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return cells[index];
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String identifier() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool asBarrier() => true;
|
||||
}
|
||||
|
||||
class _GridPropertyCell extends StatelessWidget {
|
||||
const _GridPropertyCell({Key? key}) : super(key: key);
|
||||
final Field field;
|
||||
final String gridId;
|
||||
const _GridPropertyCell({required this.gridId, required this.field, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container();
|
||||
final theme = context.watch<AppTheme>();
|
||||
|
||||
final checkmark = field.visibility
|
||||
? svgWidget('home/show', color: theme.iconColor)
|
||||
: svgWidget('home/hide', color: theme.iconColor);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(field.name, fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
|
||||
onTap: () {
|
||||
final fieldCellContext = GridFieldCellContext(
|
||||
gridId: gridId,
|
||||
field: field,
|
||||
);
|
||||
|
||||
FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldContextLoader: FieldContextLoaderAdaptor(fieldCellContext),
|
||||
).show(context, anchorDirection: AnchorDirection.bottomRight);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
FlowyIconButton(
|
||||
hoverColor: theme.hover,
|
||||
width: GridSize.typeOptionItemHeight,
|
||||
onPressed: () {
|
||||
context.read<GridPropertyBloc>().add(GridPropertyEvent.setFieldVisibility(field.id, !field.visibility));
|
||||
},
|
||||
icon: checkmark.padding(all: 6),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,34 +7,57 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
|
||||
import 'grid_property.dart';
|
||||
|
||||
class GridSettingContext {
|
||||
final String gridId;
|
||||
final List<Field> fields;
|
||||
|
||||
GridSettingContext({
|
||||
required this.gridId,
|
||||
required this.fields,
|
||||
});
|
||||
}
|
||||
|
||||
class GridSettingList extends StatelessWidget with FlowyOverlayDelegate {
|
||||
class GridSettingList extends StatelessWidget {
|
||||
final GridSettingContext settingContext;
|
||||
const GridSettingList({required this.settingContext, Key? key}) : super(key: key);
|
||||
final Function(GridSettingAction, GridSettingContext) onAction;
|
||||
const GridSettingList({required this.settingContext, required this.onAction, Key? key}) : super(key: key);
|
||||
|
||||
static void show(BuildContext context, GridSettingContext settingContext) {
|
||||
final list = GridSettingList(
|
||||
settingContext: settingContext,
|
||||
onAction: (action, settingContext) {
|
||||
switch (action) {
|
||||
case GridSettingAction.filter:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
case GridSettingAction.sortBy:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
case GridSettingAction.properties:
|
||||
GridPropertyList(gridId: settingContext.gridId, fields: settingContext.fields).show(context);
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
void show(BuildContext context) {
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: this,
|
||||
child: list,
|
||||
constraints: BoxConstraints.loose(const Size(140, 400)),
|
||||
),
|
||||
identifier: identifier(),
|
||||
identifier: list.identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.bottomRight,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
delegate: this,
|
||||
);
|
||||
}
|
||||
|
||||
@ -46,17 +69,8 @@ class GridSettingList extends StatelessWidget with FlowyOverlayDelegate {
|
||||
listenWhen: (previous, current) => previous.selectedAction != current.selectedAction,
|
||||
listener: (context, state) {
|
||||
state.selectedAction.foldLeft(null, (_, action) {
|
||||
switch (action) {
|
||||
case GridSettingAction.filter:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
case GridSettingAction.sortBy:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
case GridSettingAction.properties:
|
||||
// TODO: Handle this case.
|
||||
break;
|
||||
}
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
onAction(action, settingContext);
|
||||
});
|
||||
},
|
||||
child: BlocBuilder<GridSettingBloc, GridSettingState>(
|
||||
|
@ -1,25 +1,40 @@
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
|
||||
import 'grid_setting.dart';
|
||||
|
||||
class GridToolbar extends StatelessWidget {
|
||||
class GridToolbarContext {
|
||||
final String gridId;
|
||||
const GridToolbar({required this.gridId, Key? key}) : super(key: key);
|
||||
final List<Field> fields;
|
||||
GridToolbarContext({
|
||||
required this.gridId,
|
||||
required this.fields,
|
||||
});
|
||||
}
|
||||
|
||||
class GridToolbar extends StatelessWidget {
|
||||
final GridToolbarContext toolbarContext;
|
||||
const GridToolbar({required this.toolbarContext, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingContext = GridSettingContext(
|
||||
gridId: toolbarContext.gridId,
|
||||
fields: toolbarContext.fields,
|
||||
);
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||
_SettingButton(settingContext: GridSettingContext(gridId: gridId)),
|
||||
_SettingButton(settingContext: settingContext),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
@ -37,7 +52,7 @@ class _SettingButton extends StatelessWidget {
|
||||
return FlowyIconButton(
|
||||
hoverColor: theme.hover,
|
||||
width: 22,
|
||||
onPressed: () => GridSettingList(settingContext: settingContext).show(context),
|
||||
onPressed: () => GridSettingList.show(context, settingContext),
|
||||
icon: svgWidget("grid/setting/setting").padding(horizontal: 3, vertical: 3),
|
||||
);
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ impl ClientGridEditor {
|
||||
|
||||
pub async fn get_field_metas(&self, field_orders: Option<RepeatedFieldOrder>) -> FlowyResult<Vec<FieldMeta>> {
|
||||
let mut field_metas = self.pad.read().await.get_field_metas(field_orders)?;
|
||||
field_metas.retain(|field_meta| field_meta.visibility);
|
||||
// field_metas.retain(|field_meta| field_meta.visibility);
|
||||
Ok(field_metas)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user