mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: date field
This commit is contained in:
parent
35e27b579f
commit
d2080a6c03
@ -155,7 +155,15 @@
|
||||
"numberFieldName": "Numbers",
|
||||
"singleSelectFieldName": "Select",
|
||||
"multiSelectFieldName": "Multiselect",
|
||||
"numberFormat": " Number format"
|
||||
"numberFormat": " Number format",
|
||||
"dateFormat": " Date format",
|
||||
"dateFormatFriendly": "Month Day,Year",
|
||||
"dateFormatISO": "Year-Month-Day",
|
||||
"dateFormatLocal": "Month/Month/Day",
|
||||
"dateFormatUS": "Month/Month/Day",
|
||||
"timeFormat": " Time format",
|
||||
"timeFormatTwelveHour": "12 hour",
|
||||
"timeFormatTwentyFourHour": "24 hour"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
@ -210,16 +211,16 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<SwitchFieldTypeBloc, SwitchFieldContext, void>(
|
||||
(context, _) => SwitchFieldTypeBloc(context),
|
||||
getIt.registerFactoryParam<FieldTypeSwitchBloc, SwitchFieldContext, void>(
|
||||
(context, _) => FieldTypeSwitchBloc(context),
|
||||
);
|
||||
|
||||
getIt.registerFactory<SelectionTypeOptionBloc>(
|
||||
() => SelectionTypeOptionBloc(),
|
||||
);
|
||||
|
||||
getIt.registerFactory<DateTypeOptionBloc>(
|
||||
() => DateTypeOptionBloc(),
|
||||
getIt.registerFactoryParam<DateTypeOptionBloc, DateTypeOption, void>(
|
||||
(typeOption, _) => DateTypeOptionBloc(typeOption: typeOption),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<NumberTypeOptionBloc, NumberTypeOption, void>(
|
||||
|
@ -9,9 +9,9 @@ import 'field_service.dart';
|
||||
|
||||
part 'switch_field_type_bloc.freezed.dart';
|
||||
|
||||
class SwitchFieldTypeBloc extends Bloc<SwitchFieldTypeEvent, SwitchFieldTypeState> {
|
||||
SwitchFieldTypeBloc(SwitchFieldContext editContext) : super(SwitchFieldTypeState.initial(editContext)) {
|
||||
on<SwitchFieldTypeEvent>(
|
||||
class FieldTypeSwitchBloc extends Bloc<FieldTypeSwitchEvent, FieldTypeSwitchState> {
|
||||
FieldTypeSwitchBloc(SwitchFieldContext editContext) : super(FieldTypeSwitchState.initial(editContext)) {
|
||||
on<FieldTypeSwitchEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
toFieldType: (_ToFieldType value) async {
|
||||
@ -44,20 +44,20 @@ class SwitchFieldTypeBloc extends Bloc<SwitchFieldTypeEvent, SwitchFieldTypeStat
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SwitchFieldTypeEvent with _$SwitchFieldTypeEvent {
|
||||
const factory SwitchFieldTypeEvent.toFieldType(FieldType fieldType) = _ToFieldType;
|
||||
const factory SwitchFieldTypeEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
|
||||
class FieldTypeSwitchEvent with _$FieldTypeSwitchEvent {
|
||||
const factory FieldTypeSwitchEvent.toFieldType(FieldType fieldType) = _ToFieldType;
|
||||
const factory FieldTypeSwitchEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SwitchFieldTypeState with _$SwitchFieldTypeState {
|
||||
const factory SwitchFieldTypeState({
|
||||
class FieldTypeSwitchState with _$FieldTypeSwitchState {
|
||||
const factory FieldTypeSwitchState({
|
||||
required String gridId,
|
||||
required Field field,
|
||||
required Uint8List typeOptionData,
|
||||
}) = _SwitchFieldTypeState;
|
||||
}) = _FieldTypeSwitchState;
|
||||
|
||||
factory SwitchFieldTypeState.initial(SwitchFieldContext switchContext) => SwitchFieldTypeState(
|
||||
factory FieldTypeSwitchState.initial(SwitchFieldContext switchContext) => FieldTypeSwitchState(
|
||||
gridId: switchContext.gridId,
|
||||
field: switchContext.field,
|
||||
typeOptionData: Uint8List.fromList(switchContext.typeOptionData),
|
||||
|
@ -1,20 +1,23 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.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 'date_bloc.freezed.dart';
|
||||
|
||||
class DateTypeOptionBloc extends Bloc<DateTypeOptionEvent, DateTypeOptionState> {
|
||||
DateTypeOptionBloc() : super(DateTypeOptionState.initial()) {
|
||||
DateTypeOptionBloc({required DateTypeOption typeOption}) : super(DateTypeOptionState.initial(typeOption)) {
|
||||
on<DateTypeOptionEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_InitialField value) async {},
|
||||
event.map(
|
||||
didSelectDateFormat: (_DidSelectDateFormat value) {
|
||||
state.typeOption.dateFormat = value.format;
|
||||
emit(state);
|
||||
},
|
||||
didSelectTimeFormat: (_DidSelectTimeFormat value) {
|
||||
state.typeOption.timeFormat = value.format;
|
||||
emit(state);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -28,12 +31,15 @@ class DateTypeOptionBloc extends Bloc<DateTypeOptionEvent, DateTypeOptionState>
|
||||
|
||||
@freezed
|
||||
class DateTypeOptionEvent with _$DateTypeOptionEvent {
|
||||
const factory DateTypeOptionEvent.initial(Uint8List? typeOptionData) = _InitialField;
|
||||
const factory DateTypeOptionEvent.didSelectDateFormat(DateFormat format) = _DidSelectDateFormat;
|
||||
const factory DateTypeOptionEvent.didSelectTimeFormat(TimeFormat format) = _DidSelectTimeFormat;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DateTypeOptionState with _$DateTypeOptionState {
|
||||
const factory DateTypeOptionState() = _DateTypeOptionState;
|
||||
const factory DateTypeOptionState({
|
||||
required DateTypeOption typeOption,
|
||||
}) = _DateTypeOptionState;
|
||||
|
||||
factory DateTypeOptionState.initial() => DateTypeOptionState();
|
||||
factory DateTypeOptionState.initial(DateTypeOption typeOption) => DateTypeOptionState(typeOption: typeOption);
|
||||
}
|
||||
|
@ -9,8 +9,7 @@ class NumberTypeOptionBloc extends Bloc<NumberTypeOptionEvent, NumberTypeOptionS
|
||||
NumberTypeOptionBloc({required NumberTypeOption typeOption}) : super(NumberTypeOptionState.initial(typeOption)) {
|
||||
on<NumberTypeOptionEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_InitialField value) async {},
|
||||
event.map(
|
||||
didSelectFormat: (_DidSelectFormat value) {
|
||||
state.typeOption.format = value.format;
|
||||
emit(state);
|
||||
@ -28,7 +27,6 @@ class NumberTypeOptionBloc extends Bloc<NumberTypeOptionEvent, NumberTypeOptionS
|
||||
|
||||
@freezed
|
||||
class NumberTypeOptionEvent with _$NumberTypeOptionEvent {
|
||||
const factory NumberTypeOptionEvent.initial() = _InitialField;
|
||||
const factory NumberTypeOptionEvent.didSelectFormat(NumberFormat format) = _DidSelectFormat;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@ class GridSize {
|
||||
static double get trailHeaderPadding => 140 * scale;
|
||||
static double get headerContainerPadding => 0 * scale;
|
||||
static double get cellContentPadding => 10 * scale;
|
||||
static double get typeOptionItemHeight => 30 * scale;
|
||||
static double get typeOptionSeparatorHeight => 6 * scale;
|
||||
|
||||
//
|
||||
static EdgeInsets get headerContentInsets => EdgeInsets.symmetric(
|
||||
@ -26,6 +28,11 @@ class GridSize {
|
||||
vertical: GridSize.cellContentPadding,
|
||||
);
|
||||
|
||||
static EdgeInsets get typeOptionContentInsets => const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
);
|
||||
|
||||
static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB(
|
||||
0,
|
||||
GridSize.headerContainerPadding,
|
||||
|
@ -4,7 +4,6 @@ import 'package:app_flowy/workspace/application/grid/field/switch_field_type_blo
|
||||
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';
|
||||
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 'field_name_input.dart';
|
||||
@ -61,7 +60,6 @@ class _CreateFieldPannelWidget extends StatelessWidget {
|
||||
const _FieldNameTextField(),
|
||||
const VSpace(10),
|
||||
_FieldTypeSwitcher(SwitchFieldContext(state.gridId, field, state.typeOptionData)),
|
||||
const VSpace(10),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -1,26 +1,29 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
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_type_list.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/selection.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_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pbserver.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart';
|
||||
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_type_list.dart';
|
||||
|
||||
import 'type_option/number.dart';
|
||||
|
||||
typedef SelectFieldCallback = void Function(Field, Uint8List);
|
||||
|
||||
class FieldTypeSwitcher extends StatelessWidget {
|
||||
class FieldTypeSwitcher extends StatefulWidget {
|
||||
final SwitchFieldContext switchContext;
|
||||
final SelectFieldCallback onSelected;
|
||||
|
||||
@ -30,25 +33,27 @@ class FieldTypeSwitcher extends StatelessWidget {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<FieldTypeSwitcher> createState() => _FieldTypeSwitcherState();
|
||||
}
|
||||
|
||||
class _FieldTypeSwitcherState extends State<FieldTypeSwitcher> {
|
||||
String? currentOverlayIdentifier;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SwitchFieldTypeBloc>(param1: switchContext),
|
||||
child: BlocBuilder<SwitchFieldTypeBloc, SwitchFieldTypeState>(
|
||||
create: (context) => getIt<FieldTypeSwitchBloc>(param1: widget.switchContext),
|
||||
child: BlocBuilder<FieldTypeSwitchBloc, FieldTypeSwitchState>(
|
||||
builder: (context, state) {
|
||||
List<Widget> children = [
|
||||
_switchFieldTypeButton(context, state.field),
|
||||
];
|
||||
List<Widget> children = [_switchFieldTypeButton(context, state.field)];
|
||||
|
||||
final builder = _makeTypeOptionBuild(
|
||||
final typeOptionWidget = _typeOptionWidget(
|
||||
context: context,
|
||||
fieldType: state.field.fieldType,
|
||||
typeOptionData: state.typeOptionData,
|
||||
typeOptionDataCallback: (newTypeOptionData) {
|
||||
context.read<SwitchFieldTypeBloc>().add(SwitchFieldTypeEvent.didUpdateTypeOptionData(newTypeOptionData));
|
||||
},
|
||||
data: state.typeOptionData,
|
||||
);
|
||||
|
||||
final typeOptionWidget = builder.customWidget;
|
||||
if (typeOptionWidget != null) {
|
||||
children.add(typeOptionWidget);
|
||||
}
|
||||
@ -65,53 +70,100 @@ class FieldTypeSwitcher extends StatelessWidget {
|
||||
Widget _switchFieldTypeButton(BuildContext context, Field field) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return SizedBox(
|
||||
height: 36,
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(field.fieldType.title(), fontSize: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
hoverColor: theme.hover,
|
||||
onTap: () => FieldTypeList.show(context, (fieldType) {
|
||||
context.read<SwitchFieldTypeBloc>().add(SwitchFieldTypeEvent.toFieldType(fieldType));
|
||||
}),
|
||||
onTap: () {
|
||||
final list = FieldTypeList(onSelectField: (fieldType) {
|
||||
context.read<FieldTypeSwitchBloc>().add(FieldTypeSwitchEvent.toFieldType(fieldType));
|
||||
});
|
||||
_showOverlay(context, FieldTypeList.identifier(), list);
|
||||
},
|
||||
leftIcon: svg(field.fieldType.iconName(), color: theme.iconColor),
|
||||
rightIcon: svg("grid/more", color: theme.iconColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget? _typeOptionWidget({
|
||||
required BuildContext context,
|
||||
required FieldType fieldType,
|
||||
required TypeOptionData data,
|
||||
}) {
|
||||
final delegate = TypeOptionOperationDelegate(
|
||||
didUpdateTypeOptionData: (data) {
|
||||
context.read<FieldTypeSwitchBloc>().add(FieldTypeSwitchEvent.didUpdateTypeOptionData(data));
|
||||
},
|
||||
requireToShowOverlay: _showOverlay,
|
||||
);
|
||||
final builder = _makeTypeOptionBuild(fieldType: fieldType, data: data, delegate: delegate);
|
||||
return builder.customWidget;
|
||||
}
|
||||
|
||||
void _showOverlay(BuildContext context, String identifier, Widget child) {
|
||||
if (currentOverlayIdentifier != null) {
|
||||
FlowyOverlay.of(context).remove(currentOverlayIdentifier!);
|
||||
}
|
||||
|
||||
currentOverlayIdentifier = identifier;
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: child,
|
||||
constraints: BoxConstraints.loose(const Size(240, 400)),
|
||||
),
|
||||
identifier: identifier,
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.leftWithCenterAligned,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
anchorOffset: const Offset(-20, 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypeOptionBuilder {
|
||||
Widget? get customWidget;
|
||||
}
|
||||
|
||||
TypeOptionBuilder _makeTypeOptionBuild({
|
||||
required FieldType fieldType,
|
||||
required TypeOptionData data,
|
||||
required TypeOptionOperationDelegate delegate,
|
||||
}) {
|
||||
switch (fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return CheckboxTypeOptionBuilder(data);
|
||||
case FieldType.DateTime:
|
||||
return DateTypeOptionBuilder(data, delegate);
|
||||
case FieldType.MultiSelect:
|
||||
return MultiSelectTypeOptionBuilder(data);
|
||||
case FieldType.Number:
|
||||
return NumberTypeOptionBuilder(data, delegate);
|
||||
case FieldType.RichText:
|
||||
return RichTextTypeOptionBuilder(data);
|
||||
case FieldType.SingleSelect:
|
||||
return SingleSelectTypeOptionBuilder(data);
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypeOptionWidget extends StatelessWidget {
|
||||
const TypeOptionWidget({Key? key}) : super(key: key);
|
||||
}
|
||||
|
||||
typedef TypeOptionData = Uint8List;
|
||||
typedef TypeOptionDataCallback = void Function(TypeOptionData typeOptionData);
|
||||
typedef ShowOverlayCallback = void Function(BuildContext anchorContext, String overlayIdentifier, Widget child);
|
||||
|
||||
TypeOptionBuilder _makeTypeOptionBuild({
|
||||
required FieldType fieldType,
|
||||
required TypeOptionData typeOptionData,
|
||||
required TypeOptionDataCallback typeOptionDataCallback,
|
||||
}) {
|
||||
switch (fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return CheckboxTypeOptionBuilder(typeOptionData);
|
||||
case FieldType.DateTime:
|
||||
return DateTypeOptionBuilder(typeOptionData);
|
||||
case FieldType.MultiSelect:
|
||||
return MultiSelectTypeOptionBuilder(typeOptionData);
|
||||
case FieldType.Number:
|
||||
return NumberTypeOptionBuilder(typeOptionData, typeOptionDataCallback);
|
||||
case FieldType.RichText:
|
||||
return RichTextTypeOptionBuilder(typeOptionData);
|
||||
case FieldType.SingleSelect:
|
||||
return SingleSelectTypeOptionBuilder(typeOptionData);
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
class TypeOptionOperationDelegate {
|
||||
TypeOptionDataCallback didUpdateTypeOptionData;
|
||||
ShowOverlayCallback requireToShowOverlay;
|
||||
TypeOptionOperationDelegate({
|
||||
required this.didUpdateTypeOptionData,
|
||||
required this.requireToShowOverlay,
|
||||
});
|
||||
}
|
||||
|
||||
class RichTextTypeOptionBuilder extends TypeOptionBuilder {
|
||||
@ -123,27 +175,6 @@ class RichTextTypeOptionBuilder extends TypeOptionBuilder {
|
||||
Widget? get customWidget => null;
|
||||
}
|
||||
|
||||
class DateTypeOptionBuilder extends TypeOptionBuilder {
|
||||
DateTypeOption typeOption;
|
||||
|
||||
DateTypeOptionBuilder(TypeOptionData typeOptionData) : typeOption = DateTypeOption.fromBuffer(typeOptionData);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => const DateTypeOptionWidget();
|
||||
}
|
||||
|
||||
class DateTypeOptionWidget extends TypeOptionWidget {
|
||||
const DateTypeOptionWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<DateTypeOptionBloc>(),
|
||||
child: Container(height: 80, color: Colors.red),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CheckboxTypeOptionBuilder extends TypeOptionBuilder {
|
||||
CheckboxTypeOption typeOption;
|
||||
|
||||
@ -152,47 +183,3 @@ class CheckboxTypeOptionBuilder extends TypeOptionBuilder {
|
||||
@override
|
||||
Widget? get customWidget => null;
|
||||
}
|
||||
|
||||
class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
|
||||
SingleSelectTypeOption typeOption;
|
||||
|
||||
SingleSelectTypeOptionBuilder(TypeOptionData typeOptionData)
|
||||
: typeOption = SingleSelectTypeOption.fromBuffer(typeOptionData);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => const SingleSelectTypeOptionWidget();
|
||||
}
|
||||
|
||||
class SingleSelectTypeOptionWidget extends TypeOptionWidget {
|
||||
const SingleSelectTypeOptionWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SelectionTypeOptionBloc>(),
|
||||
child: Container(height: 100, color: Colors.yellow),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiSelectTypeOptionBuilder extends TypeOptionBuilder {
|
||||
MultiSelectTypeOption typeOption;
|
||||
|
||||
MultiSelectTypeOptionBuilder(TypeOptionData typeOptionData)
|
||||
: typeOption = MultiSelectTypeOption.fromBuffer(typeOptionData);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => const MultiSelectTypeOptionWidget();
|
||||
}
|
||||
|
||||
class MultiSelectTypeOptionWidget extends TypeOptionWidget {
|
||||
const MultiSelectTypeOptionWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SelectionTypeOptionBloc>(),
|
||||
child: Container(height: 100, color: Colors.blue),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,13 @@ class FieldTypeList extends StatelessWidget {
|
||||
|
||||
static void show(BuildContext context, SelectFieldCallback onSelectField) {
|
||||
final list = FieldTypeList(onSelectField: onSelectField);
|
||||
FieldTypeList.hide(context);
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: list,
|
||||
constraints: BoxConstraints.loose(const Size(140, 300)),
|
||||
),
|
||||
identifier: list.identifier(),
|
||||
identifier: FieldTypeList.identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.leftWithCenterAligned,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
@ -32,6 +33,10 @@ class FieldTypeList extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
static void hide(BuildContext context) {
|
||||
FlowyOverlay.of(context).remove(FieldTypeList.identifier());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cells = FieldType.values.map((fieldType) {
|
||||
@ -39,7 +44,7 @@ class FieldTypeList extends StatelessWidget {
|
||||
fieldType: fieldType,
|
||||
onSelectField: (fieldType) {
|
||||
onSelectField(fieldType);
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
FlowyOverlay.of(context).remove(FieldTypeList.identifier());
|
||||
},
|
||||
);
|
||||
}).toList();
|
||||
@ -58,8 +63,8 @@ class FieldTypeList extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
String identifier() {
|
||||
return toString();
|
||||
static String identifier() {
|
||||
return (FieldTypeList).toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,229 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/date_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_tyep_switcher.dart';
|
||||
import 'package:easy_localization/easy_localization.dart' hide DateFormat;
|
||||
import 'package:app_flowy/generated/locale_keys.g.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:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DateTypeOptionBuilder extends TypeOptionBuilder {
|
||||
DateTypeOption typeOption;
|
||||
TypeOptionOperationDelegate delegate;
|
||||
|
||||
DateTypeOptionBuilder(TypeOptionData typeOptionData, this.delegate)
|
||||
: typeOption = DateTypeOption.fromBuffer(typeOptionData);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => DateTypeOptionWidget(
|
||||
typeOption: typeOption,
|
||||
operationDelegate: delegate,
|
||||
);
|
||||
}
|
||||
|
||||
class DateTypeOptionWidget extends TypeOptionWidget {
|
||||
final DateTypeOption typeOption;
|
||||
final TypeOptionOperationDelegate operationDelegate;
|
||||
const DateTypeOptionWidget({required this.typeOption, required this.operationDelegate, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<DateTypeOptionBloc>(param1: typeOption),
|
||||
child: BlocConsumer<DateTypeOptionBloc, DateTypeOptionState>(
|
||||
listener: (context, state) => operationDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()),
|
||||
builder: (context, state) {
|
||||
return Column(children: [
|
||||
_dateFormatButton(context),
|
||||
_timeFormatButton(context),
|
||||
]);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _dateFormatButton(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(LocaleKeys.grid_field_dateFormat.tr(), fontSize: 12),
|
||||
padding: GridSize.typeOptionContentInsets,
|
||||
hoverColor: theme.hover,
|
||||
onTap: () {
|
||||
final list = DateFormatList(onSelected: (format) {
|
||||
context.read<DateTypeOptionBloc>().add(DateTypeOptionEvent.didSelectDateFormat(format));
|
||||
});
|
||||
operationDelegate.requireToShowOverlay(context, list.identifier(), list);
|
||||
},
|
||||
rightIcon: svg("grid/more", color: theme.iconColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _timeFormatButton(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(LocaleKeys.grid_field_timeFormat.tr(), fontSize: 12),
|
||||
padding: GridSize.typeOptionContentInsets,
|
||||
hoverColor: theme.hover,
|
||||
onTap: () {
|
||||
final list = TimeFormatList(onSelected: (format) {
|
||||
context.read<DateTypeOptionBloc>().add(DateTypeOptionEvent.didSelectTimeFormat(format));
|
||||
});
|
||||
operationDelegate.requireToShowOverlay(context, list.identifier(), list);
|
||||
},
|
||||
rightIcon: svg("grid/more", color: theme.iconColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DateFormatList extends StatelessWidget {
|
||||
final Function(DateFormat format) onSelected;
|
||||
const DateFormatList({required this.onSelected, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formatItems = DateFormat.values.map((format) {
|
||||
return DateFormatItem(
|
||||
dateFormat: format,
|
||||
onSelected: (format) {
|
||||
onSelected(format);
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
});
|
||||
}).toList();
|
||||
|
||||
return SizedBox(
|
||||
width: 180,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemCount: formatItems.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return formatItems[index];
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String identifier() {
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
||||
class DateFormatItem extends StatelessWidget {
|
||||
final DateFormat dateFormat;
|
||||
final Function(DateFormat format) onSelected;
|
||||
const DateFormatItem({required this.dateFormat, required this.onSelected, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(dateFormat.title(), fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
onTap: () => onSelected(dateFormat),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension DateFormatExtension on DateFormat {
|
||||
String title() {
|
||||
switch (this) {
|
||||
case DateFormat.Friendly:
|
||||
return LocaleKeys.grid_field_dateFormatFriendly.tr();
|
||||
case DateFormat.ISO:
|
||||
return LocaleKeys.grid_field_dateFormatISO.tr();
|
||||
case DateFormat.Local:
|
||||
return LocaleKeys.grid_field_dateFormatLocal.tr();
|
||||
case DateFormat.US:
|
||||
return LocaleKeys.grid_field_dateFormatUS.tr();
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TimeFormatList extends StatelessWidget {
|
||||
final Function(TimeFormat format) onSelected;
|
||||
const TimeFormatList({required this.onSelected, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formatItems = TimeFormat.values.map((format) {
|
||||
return TimeFormatItem(
|
||||
timeFormat: format,
|
||||
onSelected: (format) {
|
||||
onSelected(format);
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
});
|
||||
}).toList();
|
||||
|
||||
return SizedBox(
|
||||
width: 120,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemCount: formatItems.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return formatItems[index];
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String identifier() {
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
||||
class TimeFormatItem extends StatelessWidget {
|
||||
final TimeFormat timeFormat;
|
||||
final Function(TimeFormat format) onSelected;
|
||||
const TimeFormatItem({required this.timeFormat, required this.onSelected, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(timeFormat.title(), fontSize: 12),
|
||||
hoverColor: theme.hover,
|
||||
onTap: () => onSelected(timeFormat),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension TimeFormatExtension on TimeFormat {
|
||||
String title() {
|
||||
switch (this) {
|
||||
case TimeFormat.TwelveHour:
|
||||
return LocaleKeys.grid_field_timeFormatTwelveHour.tr();
|
||||
case TimeFormat.TwentyFourHour:
|
||||
return LocaleKeys.grid_field_timeFormatTwentyFourHour.tr();
|
||||
default:
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/number_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_tyep_switcher.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
@ -15,24 +16,24 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
|
||||
class NumberTypeOptionBuilder extends TypeOptionBuilder {
|
||||
NumberTypeOption typeOption;
|
||||
TypeOptionDataCallback typeOptionDataCallback;
|
||||
TypeOptionOperationDelegate delegate;
|
||||
|
||||
NumberTypeOptionBuilder(
|
||||
TypeOptionData typeOptionData,
|
||||
this.typeOptionDataCallback,
|
||||
this.delegate,
|
||||
) : typeOption = NumberTypeOption.fromBuffer(typeOptionData);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => NumberTypeOptionWidget(
|
||||
typeOption: typeOption,
|
||||
updateCallback: typeOptionDataCallback,
|
||||
operationDelegate: delegate,
|
||||
);
|
||||
}
|
||||
|
||||
class NumberTypeOptionWidget extends TypeOptionWidget {
|
||||
final TypeOptionDataCallback updateCallback;
|
||||
final TypeOptionOperationDelegate operationDelegate;
|
||||
final NumberTypeOption typeOption;
|
||||
const NumberTypeOptionWidget({required this.typeOption, required this.updateCallback, Key? key}) : super(key: key);
|
||||
const NumberTypeOptionWidget({required this.typeOption, required this.operationDelegate, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -40,18 +41,19 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<NumberTypeOptionBloc>(param1: typeOption),
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: BlocConsumer<NumberTypeOptionBloc, NumberTypeOptionState>(
|
||||
listener: (context, state) => updateCallback(state.typeOption.writeToBuffer()),
|
||||
listener: (context, state) => operationDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()),
|
||||
builder: (context, state) {
|
||||
return FlowyButton(
|
||||
text: FlowyText.medium(LocaleKeys.grid_field_numberFormat.tr(), fontSize: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
padding: GridSize.typeOptionContentInsets,
|
||||
hoverColor: theme.hover,
|
||||
onTap: () {
|
||||
NumberFormatList.show(context, (format) {
|
||||
final list = NumberFormatList(onSelected: (format) {
|
||||
context.read<NumberTypeOptionBloc>().add(NumberTypeOptionEvent.didSelectFormat(format));
|
||||
});
|
||||
operationDelegate.requireToShowOverlay(context, list.identifier(), list);
|
||||
},
|
||||
rightIcon: svg("grid/more", color: theme.iconColor),
|
||||
);
|
||||
@ -68,21 +70,6 @@ class NumberFormatList extends StatelessWidget {
|
||||
final _SelectNumberFormatCallback onSelected;
|
||||
const NumberFormatList({required this.onSelected, Key? key}) : super(key: key);
|
||||
|
||||
static void show(BuildContext context, _SelectNumberFormatCallback onSelected) {
|
||||
final list = NumberFormatList(onSelected: onSelected);
|
||||
FlowyOverlay.of(context).insertWithAnchor(
|
||||
widget: OverlayContainer(
|
||||
child: list,
|
||||
constraints: BoxConstraints.loose(const Size(140, 300)),
|
||||
),
|
||||
identifier: list.identifier(),
|
||||
anchorContext: context,
|
||||
anchorDirection: AnchorDirection.leftWithCenterAligned,
|
||||
style: FlowyOverlayStyle(blur: false),
|
||||
anchorOffset: const Offset(-20, 0),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formatItems = NumberFormat.values.map((format) {
|
||||
@ -94,16 +81,19 @@ class NumberFormatList extends StatelessWidget {
|
||||
});
|
||||
}).toList();
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
separatorBuilder: (context, index) {
|
||||
return const VSpace(10);
|
||||
},
|
||||
itemCount: formatItems.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return formatItems[index];
|
||||
},
|
||||
return SizedBox(
|
||||
width: 120,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
itemCount: formatItems.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return formatItems[index];
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,50 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/field/type_option/selection_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
|
||||
SingleSelectTypeOption typeOption;
|
||||
|
||||
SingleSelectTypeOptionBuilder(TypeOptionData typeOptionData)
|
||||
: typeOption = SingleSelectTypeOption.fromBuffer(typeOptionData);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => const SingleSelectTypeOptionWidget();
|
||||
}
|
||||
|
||||
class SingleSelectTypeOptionWidget extends TypeOptionWidget {
|
||||
const SingleSelectTypeOptionWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SelectionTypeOptionBloc>(),
|
||||
child: Container(height: 100, color: Colors.yellow),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiSelectTypeOptionBuilder extends TypeOptionBuilder {
|
||||
MultiSelectTypeOption typeOption;
|
||||
|
||||
MultiSelectTypeOptionBuilder(TypeOptionData typeOptionData)
|
||||
: typeOption = MultiSelectTypeOption.fromBuffer(typeOptionData);
|
||||
|
||||
@override
|
||||
Widget? get customWidget => const MultiSelectTypeOptionWidget();
|
||||
}
|
||||
|
||||
class MultiSelectTypeOptionWidget extends TypeOptionWidget {
|
||||
const MultiSelectTypeOptionWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SelectionTypeOptionBloc>(),
|
||||
child: Container(height: 100, color: Colors.blue),
|
||||
);
|
||||
}
|
||||
}
|
@ -182,7 +182,9 @@ class FlowyOverlayState extends State<FlowyOverlay> {
|
||||
void remove(String identifier) {
|
||||
setState(() {
|
||||
final index = _overlayList.indexWhere((ele) => ele.value2 == identifier);
|
||||
_overlayList.removeAt(index).value3?.didRemove();
|
||||
if (index != -1) {
|
||||
_overlayList.removeAt(index).value3?.didRemove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user