mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: config hover
This commit is contained in:
parent
eeba6884ce
commit
f66adb53c9
@ -177,7 +177,8 @@
|
||||
},
|
||||
"row": {
|
||||
"duplicate": "Duplicate",
|
||||
"delete": "Delete"
|
||||
"delete": "Delete",
|
||||
"textPlaceholder": "Empty"
|
||||
},
|
||||
"selectOption": {
|
||||
"purpleColor": "Purple",
|
||||
|
@ -170,7 +170,6 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
|
||||
getIt.registerFactoryParam<TextCellBloc, GridCell, void>(
|
||||
(cellData, _) => TextCellBloc(
|
||||
service: CellService(),
|
||||
cellData: cellData,
|
||||
),
|
||||
);
|
||||
@ -189,7 +188,7 @@ void _resolveGridDeps(GetIt getIt) {
|
||||
|
||||
getIt.registerFactoryParam<DateCellBloc, GridCell, void>(
|
||||
(cellData, _) => DateCellBloc(
|
||||
cellIdentifier: cellData,
|
||||
cellData: cellData,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -11,13 +11,13 @@ part 'checkbox_cell_bloc.freezed.dart';
|
||||
|
||||
class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
final CellService _service;
|
||||
final CellListener _listener;
|
||||
final CellListener _cellListener;
|
||||
|
||||
CheckboxCellBloc({
|
||||
required CellService service,
|
||||
required GridCell cellData,
|
||||
}) : _service = service,
|
||||
_listener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
_cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
super(CheckboxCellState.initial(cellData)) {
|
||||
on<CheckboxCellEvent>(
|
||||
(event, emit) async {
|
||||
@ -38,18 +38,18 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _listener.stop();
|
||||
await _cellListener.stop();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_listener.updateCellNotifier?.addPublishListener((result) {
|
||||
_cellListener.updateCellNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(notificationData) async => await _loadCellData(),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_listener.start();
|
||||
_cellListener.start();
|
||||
}
|
||||
|
||||
Future<void> _loadCellData() async {
|
||||
|
@ -15,11 +15,11 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||
final CellListener _cellListener;
|
||||
final SingleFieldListener _fieldListener;
|
||||
|
||||
DateCellBloc({required GridCell cellIdentifier})
|
||||
DateCellBloc({required GridCell cellData})
|
||||
: _service = CellService(),
|
||||
_cellListener = CellListener(rowId: cellIdentifier.rowId, fieldId: cellIdentifier.field.id),
|
||||
_fieldListener = SingleFieldListener(fieldId: cellIdentifier.field.id),
|
||||
super(DateCellState.initial(cellIdentifier)) {
|
||||
_cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
_fieldListener = SingleFieldListener(fieldId: cellData.field.id),
|
||||
super(DateCellState.initial(cellData)) {
|
||||
on<DateCellEvent>(
|
||||
(event, emit) async {
|
||||
event.map(
|
||||
|
@ -1,22 +1,29 @@
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'cell_listener.dart';
|
||||
import 'cell_service.dart';
|
||||
|
||||
part 'text_cell_bloc.freezed.dart';
|
||||
|
||||
class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
final CellService service;
|
||||
final CellService _service;
|
||||
final CellListener _cellListener;
|
||||
|
||||
TextCellBloc({
|
||||
required this.service,
|
||||
required GridCell cellData,
|
||||
}) : super(TextCellState.initial(cellData)) {
|
||||
}) : _service = CellService(),
|
||||
_cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
|
||||
super(TextCellState.initial(cellData)) {
|
||||
on<TextCellEvent>(
|
||||
(event, emit) async {
|
||||
await event.map(
|
||||
initial: (_InitialCell value) async {},
|
||||
initial: (_InitialCell value) async {
|
||||
_startListening();
|
||||
},
|
||||
updateText: (_UpdateText value) {
|
||||
updateCellContent(value.text);
|
||||
emit(state.copyWith(content: value.text));
|
||||
@ -27,16 +34,28 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
content: value.cellData.cell?.content ?? "",
|
||||
));
|
||||
},
|
||||
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
|
||||
emit(state.copyWith(
|
||||
cellData: state.cellData.copyWith(cell: value.cell),
|
||||
content: value.cell.content,
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _cellListener.stop();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void updateCellContent(String content) {
|
||||
final fieldId = state.cellData.field.id;
|
||||
final gridId = state.cellData.gridId;
|
||||
final rowId = state.cellData.rowId;
|
||||
service.updateCell(
|
||||
_service.updateCell(
|
||||
data: content,
|
||||
fieldId: fieldId,
|
||||
gridId: gridId,
|
||||
@ -44,9 +63,29 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
return super.close();
|
||||
void _startListening() {
|
||||
_cellListener.updateCellNotifier?.addPublishListener((result) {
|
||||
result.fold(
|
||||
(notificationData) async => await _loadCellData(),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
_cellListener.start();
|
||||
}
|
||||
|
||||
Future<void> _loadCellData() async {
|
||||
final result = await _service.getCell(
|
||||
gridId: state.cellData.gridId,
|
||||
fieldId: state.cellData.field.id,
|
||||
rowId: state.cellData.rowId,
|
||||
);
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
result.fold(
|
||||
(cell) => add(TextCellEvent.didReceiveCellUpdate(cell)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +93,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
|
||||
class TextCellEvent with _$TextCellEvent {
|
||||
const factory TextCellEvent.initial() = _InitialCell;
|
||||
const factory TextCellEvent.didReceiveCellData(GridCell cellData) = _DidReceiveCellData;
|
||||
const factory TextCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||
const factory TextCellEvent.updateText(String text) = _UpdateText;
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,11 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
await event.map(
|
||||
initial: (_Initial value) async {
|
||||
await _startListening();
|
||||
_loadCellData();
|
||||
},
|
||||
didReceiveCellDatas: (_DidReceiveCellDatas value) {
|
||||
emit(state.copyWith(cellDatas: value.cellDatas));
|
||||
},
|
||||
didReceiveCellDatas: (_DidReceiveCellDatas value) {},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -40,16 +43,16 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
Future<void> _startListening() async {
|
||||
_rowListenFn = _rowCache.addRowListener(
|
||||
rowId: rowData.rowId,
|
||||
onUpdated: (cellDatas) => add(RowDetailEvent.didReceiveCellDatas(cellDatas)),
|
||||
onUpdated: (cellDatas) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadRow(Emitter<RowDetailState> emit) async {
|
||||
Future<void> _loadCellData() async {
|
||||
final data = _rowCache.loadCellData(rowData.rowId);
|
||||
data.foldRight(null, (cellDatas, _) {
|
||||
data.foldRight(null, (cellDataMap, _) {
|
||||
if (!isClosed) {
|
||||
add(RowDetailEvent.didReceiveCellDatas(cellDatas));
|
||||
add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList()));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -58,16 +61,16 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
|
||||
@freezed
|
||||
class RowDetailEvent with _$RowDetailEvent {
|
||||
const factory RowDetailEvent.initial() = _Initial;
|
||||
const factory RowDetailEvent.didReceiveCellDatas(CellDataMap cellData) = _DidReceiveCellDatas;
|
||||
const factory RowDetailEvent.didReceiveCellDatas(List<GridCell> cellDatas) = _DidReceiveCellDatas;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RowDetailState with _$RowDetailState {
|
||||
const factory RowDetailState({
|
||||
required Option<CellDataMap> cellDataMap,
|
||||
required List<GridCell> cellDatas,
|
||||
}) = _RowDetailState;
|
||||
|
||||
factory RowDetailState.initial() => RowDetailState(
|
||||
cellDataMap: none(),
|
||||
cellDatas: List.empty(),
|
||||
);
|
||||
}
|
||||
|
@ -82,16 +82,14 @@ class CreateItem extends StatelessWidget {
|
||||
|
||||
return FlowyHover(
|
||||
style: config,
|
||||
builder: (context, onHover) {
|
||||
return GestureDetector(
|
||||
onTap: () => onSelected(pluginBuilder),
|
||||
child: FlowyText.medium(
|
||||
pluginBuilder.menuName,
|
||||
color: theme.textColor,
|
||||
fontSize: 12,
|
||||
).padding(horizontal: 10, vertical: 6),
|
||||
);
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () => onSelected(pluginBuilder),
|
||||
child: FlowyText.medium(
|
||||
pluginBuilder.menuName,
|
||||
color: theme.textColor,
|
||||
fontSize: 12,
|
||||
).padding(horizontal: 10, vertical: 6),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -95,9 +95,11 @@ class _MenuAppState extends State<MenuApp> {
|
||||
Widget _renderViewSection(AppDataNotifier notifier) {
|
||||
return MultiProvider(
|
||||
providers: [ChangeNotifierProvider.value(value: notifier)],
|
||||
child: Consumer(builder: (context, AppDataNotifier notifier, child) {
|
||||
return ViewSection(appData: notifier);
|
||||
}),
|
||||
child: Consumer(
|
||||
builder: (context, AppDataNotifier notifier, child) {
|
||||
return ViewSection(appData: notifier);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/grid_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
|
||||
@ -214,8 +213,7 @@ class _GridRowsState extends State<_GridRows> {
|
||||
key: _key,
|
||||
initialItemCount: context.read<GridBloc>().state.rows.length,
|
||||
itemBuilder: (BuildContext context, int index, Animation<double> animation) {
|
||||
final rowData = context.read<GridBloc>().state.rows[index];
|
||||
return _renderRow(context, rowData, animation);
|
||||
return _renderRow(context, state.rows[index], animation);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -1,13 +1,17 @@
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'checkbox_cell.dart';
|
||||
import 'date_cell.dart';
|
||||
import 'number_cell.dart';
|
||||
import 'selection_cell/selection_cell.dart';
|
||||
import 'text_cell.dart';
|
||||
|
||||
Widget buildGridCell(GridCell cellData) {
|
||||
GridCellWidget buildGridCell(GridCell cellData, {GridCellStyle? style}) {
|
||||
final key = ValueKey(cellData.field.id + cellData.rowId);
|
||||
switch (cellData.field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
@ -19,7 +23,7 @@ Widget buildGridCell(GridCell cellData) {
|
||||
case FieldType.Number:
|
||||
return NumberCell(cellData: cellData, key: key);
|
||||
case FieldType.RichText:
|
||||
return GridTextCell(cellData: cellData, key: key);
|
||||
return GridTextCell(cellData: cellData, key: key, style: style);
|
||||
case FieldType.SingleSelect:
|
||||
return SingleSelectCell(cellData: cellData, key: key);
|
||||
default:
|
||||
@ -35,3 +39,116 @@ class BlankCell extends StatelessWidget {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GridCellWidget extends HoverWidget {
|
||||
@override
|
||||
final ValueNotifier<bool> onFocus = ValueNotifier<bool>(false);
|
||||
GridCellWidget({Key? key}) : super(key: key);
|
||||
}
|
||||
|
||||
abstract class B {
|
||||
ValueNotifier<bool> get onFocus;
|
||||
}
|
||||
|
||||
abstract class GridCellStyle {}
|
||||
|
||||
//
|
||||
abstract class HoverWidget extends StatefulWidget {
|
||||
const HoverWidget({Key? key}) : super(key: key);
|
||||
|
||||
ValueNotifier<bool> get onFocus;
|
||||
}
|
||||
|
||||
class FlowyHover2 extends StatefulWidget {
|
||||
final HoverWidget child;
|
||||
const FlowyHover2({required this.child, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<FlowyHover2> createState() => _FlowyHover2State();
|
||||
}
|
||||
|
||||
class _FlowyHover2State extends State<FlowyHover2> {
|
||||
late FlowyHoverState _hoverState;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_hoverState = FlowyHoverState();
|
||||
widget.child.onFocus.addListener(() {
|
||||
_hoverState.onFocus = widget.child.onFocus.value;
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hoverState.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider.value(
|
||||
value: _hoverState,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
opaque: false,
|
||||
onEnter: (p) => setState(() => _hoverState.onHover = true),
|
||||
onExit: (p) => setState(() => _hoverState.onHover = false),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
const _HoverBackground(),
|
||||
widget.child,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HoverBackground extends StatelessWidget {
|
||||
const _HoverBackground({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return Consumer<FlowyHoverState>(
|
||||
builder: (context, state, child) {
|
||||
if (state.onHover || state.onFocus) {
|
||||
return FlowyHoverContainer(
|
||||
style: HoverStyle(
|
||||
borderRadius: Corners.s6Border,
|
||||
hoverColor: theme.shader6,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FlowyHoverState extends ChangeNotifier {
|
||||
bool _onHover = false;
|
||||
bool _onFocus = false;
|
||||
|
||||
set onHover(bool value) {
|
||||
if (_onHover != value) {
|
||||
_onHover = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool get onHover => _onHover;
|
||||
|
||||
set onFocus(bool value) {
|
||||
if (_onFocus != value) {
|
||||
_onFocus = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool get onFocus => _onFocus;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class CellStateNotifier extends ChangeNotifier {
|
||||
bool _isFocus = false;
|
||||
@ -28,7 +28,7 @@ class CellStateNotifier extends ChangeNotifier {
|
||||
}
|
||||
|
||||
class CellContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
final GridCellWidget child;
|
||||
final Widget? expander;
|
||||
final double width;
|
||||
const CellContainer({
|
||||
@ -46,6 +46,9 @@ class CellContainer extends StatelessWidget {
|
||||
selector: (context, notifier) => notifier.isFocus,
|
||||
builder: (context, isFocus, _) {
|
||||
Widget container = Center(child: child);
|
||||
child.onFocus.addListener(() {
|
||||
Provider.of<CellStateNotifier>(context, listen: false).isFocus = child.onFocus.value;
|
||||
});
|
||||
|
||||
if (expander != null) {
|
||||
container = _CellEnterRegion(child: container, expander: expander!);
|
||||
@ -75,16 +78,16 @@ class CellContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _CellEnterRegion extends StatelessWidget {
|
||||
final Widget expander;
|
||||
final Widget child;
|
||||
const _CellEnterRegion({required this.expander, required this.child, Key? key}) : super(key: key);
|
||||
final Widget expander;
|
||||
const _CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<CellStateNotifier, bool>(
|
||||
selector: (context, notifier) => notifier.onEnter,
|
||||
builder: (context, onEnter, _) {
|
||||
List<Widget> children = [child];
|
||||
List<Widget> children = [Expanded(child: child)];
|
||||
if (onEnter) {
|
||||
children.add(expander);
|
||||
}
|
||||
@ -93,8 +96,8 @@ class _CellEnterRegion extends StatelessWidget {
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (p) => Provider.of<CellStateNotifier>(context, listen: false).onEnter = true,
|
||||
onExit: (p) => Provider.of<CellStateNotifier>(context, listen: false).onEnter = false,
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: Row(
|
||||
// alignment: AlignmentDirectional.centerEnd,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
@ -102,35 +105,3 @@ class _CellEnterRegion extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GridCellWidget extends StatefulWidget {
|
||||
const GridCellWidget({Key? key}) : super(key: key);
|
||||
|
||||
void setFocus(BuildContext context, bool value) {
|
||||
Provider.of<CellStateNotifier>(context, listen: false).isFocus = value;
|
||||
}
|
||||
}
|
||||
|
||||
class CellFocusNode extends FocusNode {
|
||||
VoidCallback? focusCallback;
|
||||
|
||||
void addCallback(BuildContext context, VoidCallback callback) {
|
||||
if (focusCallback != null) {
|
||||
removeListener(focusCallback!);
|
||||
}
|
||||
focusCallback = () {
|
||||
Provider.of<CellStateNotifier>(context, listen: false).isFocus = hasFocus;
|
||||
callback();
|
||||
};
|
||||
|
||||
addListener(focusCallback!);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (focusCallback != null) {
|
||||
removeListener(focusCallback!);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,12 @@ import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class CheckboxCell extends StatefulWidget {
|
||||
class CheckboxCell extends GridCellWidget {
|
||||
final GridCell cellData;
|
||||
|
||||
const CheckboxCell({
|
||||
CheckboxCell({
|
||||
required this.cellData,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
@ -1,17 +1,22 @@
|
||||
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/cell/cell_container.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
import 'cell_builder.dart';
|
||||
|
||||
abstract class GridCellDelegate {
|
||||
void onFocus(bool isFocus);
|
||||
GridCellDelegate get delegate;
|
||||
}
|
||||
|
||||
class DateCell extends GridCellWidget {
|
||||
final GridCell cellData;
|
||||
|
||||
const DateCell({
|
||||
DateCell({
|
||||
required this.cellData,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -39,13 +44,13 @@ class _DateCellState extends State<DateCell> {
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
widget.setFocus(context, true);
|
||||
widget.onFocus.value = true;
|
||||
_CellCalendar.show(
|
||||
context,
|
||||
onSelected: (day) {
|
||||
context.read<DateCellBloc>().add(DateCellEvent.selectDay(day));
|
||||
},
|
||||
onDismissed: () => widget.setFocus(context, false),
|
||||
onDismissed: () => widget.onFocus.value = false,
|
||||
);
|
||||
},
|
||||
child: MouseRegion(
|
||||
|
@ -2,14 +2,15 @@ import 'dart:async';
|
||||
|
||||
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/cell/cell_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class NumberCell extends GridCellWidget {
|
||||
final GridCell cellData;
|
||||
|
||||
const NumberCell({
|
||||
NumberCell({
|
||||
required this.cellData,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -21,21 +22,23 @@ class NumberCell extends GridCellWidget {
|
||||
class _NumberCellState extends State<NumberCell> {
|
||||
late NumberCellBloc _cellBloc;
|
||||
late TextEditingController _controller;
|
||||
late CellFocusNode _focusNode;
|
||||
late FocusNode _focusNode;
|
||||
Timer? _delayOperation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cellBloc = getIt<NumberCellBloc>(param1: widget.cellData)..add(const NumberCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
_focusNode = CellFocusNode();
|
||||
_focusNode = FocusNode();
|
||||
_focusNode.addListener(() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
focusChanged();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_focusNode.addCallback(context, focusChanged);
|
||||
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocConsumer<NumberCellBloc, NumberCellState>(
|
||||
|
@ -1,6 +1,6 @@
|
||||
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/cell/cell_container.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@ -10,7 +10,7 @@ import 'selection_editor.dart';
|
||||
class SingleSelectCell extends GridCellWidget {
|
||||
final GridCell cellData;
|
||||
|
||||
const SingleSelectCell({
|
||||
SingleSelectCell({
|
||||
required this.cellData,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -38,13 +38,13 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
|
||||
return SizedBox.expand(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
widget.setFocus(context, true);
|
||||
widget.onFocus.value = true;
|
||||
SelectOptionCellEditor.show(
|
||||
context,
|
||||
state.cellData,
|
||||
state.options,
|
||||
state.selectedOptions,
|
||||
() => widget.setFocus(context, false),
|
||||
() => widget.onFocus.value = false,
|
||||
);
|
||||
},
|
||||
child: ClipRRect(child: Row(children: children)),
|
||||
@ -66,7 +66,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
|
||||
class MultiSelectCell extends GridCellWidget {
|
||||
final GridCell cellData;
|
||||
|
||||
const MultiSelectCell({
|
||||
MultiSelectCell({
|
||||
required this.cellData,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
@ -94,13 +94,13 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
|
||||
return SizedBox.expand(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
widget.setFocus(context, true);
|
||||
widget.onFocus.value = true;
|
||||
SelectOptionCellEditor.show(
|
||||
context,
|
||||
state.cellData,
|
||||
state.options,
|
||||
state.selectedOptions,
|
||||
() => widget.setFocus(context, false),
|
||||
() => widget.onFocus.value = false,
|
||||
);
|
||||
},
|
||||
child: ClipRRect(child: Row(children: children)),
|
||||
|
@ -1,16 +1,39 @@
|
||||
import 'dart:async';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'cell_container.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||
import 'cell_builder.dart';
|
||||
|
||||
class GridTextCellStyle extends GridCellStyle {
|
||||
String? placeholder;
|
||||
Color? hoverColor;
|
||||
bool filled;
|
||||
InputBorder? inputBorder;
|
||||
EdgeInsets? contentPadding;
|
||||
GridTextCellStyle({
|
||||
this.placeholder,
|
||||
this.hoverColor,
|
||||
this.filled = false,
|
||||
this.inputBorder,
|
||||
this.contentPadding,
|
||||
});
|
||||
}
|
||||
|
||||
class GridTextCell extends GridCellWidget {
|
||||
final GridCell cellData;
|
||||
const GridTextCell({
|
||||
late final GridTextCellStyle? cellStyle;
|
||||
GridTextCell({
|
||||
required this.cellData,
|
||||
GridCellStyle? style,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
}) : super(key: key) {
|
||||
if (style != null) {
|
||||
cellStyle = (style as GridTextCellStyle);
|
||||
} else {
|
||||
cellStyle = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
State<GridTextCell> createState() => _GridTextCellState();
|
||||
@ -19,21 +42,25 @@ class GridTextCell extends GridCellWidget {
|
||||
class _GridTextCellState extends State<GridTextCell> {
|
||||
late TextCellBloc _cellBloc;
|
||||
late TextEditingController _controller;
|
||||
late CellFocusNode _focusNode;
|
||||
late FocusNode _focusNode;
|
||||
|
||||
Timer? _delayOperation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cellBloc = getIt<TextCellBloc>(param1: widget.cellData);
|
||||
_cellBloc.add(const TextCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
_focusNode = CellFocusNode();
|
||||
_focusNode = FocusNode();
|
||||
_focusNode.addListener(() {
|
||||
widget.onFocus.value = _focusNode.hasFocus;
|
||||
focusChanged();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_focusNode.addCallback(context, focusChanged);
|
||||
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocConsumer<TextCellBloc, TextCellState>(
|
||||
@ -42,6 +69,7 @@ class _GridTextCellState extends State<GridTextCell> {
|
||||
_controller.text = state.content;
|
||||
}
|
||||
},
|
||||
buildWhen: (previous, current) => previous.content != current.content,
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
controller: _controller,
|
||||
@ -50,9 +78,13 @@ class _GridTextCellState extends State<GridTextCell> {
|
||||
onEditingComplete: () => _focusNode.unfocus(),
|
||||
maxLines: 1,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: widget.cellStyle?.contentPadding ?? EdgeInsets.zero,
|
||||
border: widget.cellStyle?.inputBorder ?? InputBorder.none,
|
||||
hintText: widget.cellStyle?.placeholder,
|
||||
hoverColor: widget.cellStyle?.hoverColor ?? Colors.transparent,
|
||||
filled: widget.cellStyle?.filled ?? false,
|
||||
fillColor: Colors.transparent,
|
||||
isDense: true,
|
||||
),
|
||||
);
|
||||
|
@ -7,6 +7,7 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_sdk/log.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 'field_type_extension.dart';
|
||||
@ -20,21 +21,21 @@ class GridFieldCell extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) => FieldCellBloc(cellContext: cellContext)..add(const FieldCellEvent.initial()),
|
||||
child: BlocBuilder<FieldCellBloc, FieldCellState>(
|
||||
builder: (context, state) {
|
||||
final button = FlowyButton(
|
||||
hoverColor: theme.shader6,
|
||||
final button = FieldCellButton(
|
||||
field: state.field,
|
||||
onTap: () => _showActionSheet(context),
|
||||
leftIcon: svgWidget(state.field.fieldType.iconName(), color: theme.iconColor),
|
||||
text: FlowyText.medium(state.field.name, fontSize: 12),
|
||||
padding: GridSize.cellContentInsets,
|
||||
);
|
||||
|
||||
const line = Positioned(top: 0, bottom: 0, right: 0, child: _DragToExpandLine());
|
||||
const line = Positioned(
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: _DragToExpandLine(),
|
||||
);
|
||||
|
||||
return _CellContainer(
|
||||
width: state.field.width.toDouble(),
|
||||
@ -125,9 +126,31 @@ class _DragToExpandLine extends StatelessWidget {
|
||||
borderRadius: BorderRadius.zero,
|
||||
contentMargin: const EdgeInsets.only(left: 5),
|
||||
),
|
||||
builder: (_, onHover) => const SizedBox(width: 2),
|
||||
child: const SizedBox(width: 2),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldCellButton extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
final Field field;
|
||||
const FieldCellButton({
|
||||
required this.field,
|
||||
required this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return FlowyButton(
|
||||
hoverColor: theme.shader6,
|
||||
onTap: onTap,
|
||||
leftIcon: svgWidget(field.fieldType.iconName(), color: theme.iconColor),
|
||||
text: FlowyText.medium(field.name, fontSize: 12),
|
||||
padding: GridSize.cellContentInsets,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ import 'package:app_flowy/startup/startup.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/field_switch_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.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';
|
||||
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:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'field_name_input.dart';
|
||||
import 'field_switcher.dart';
|
||||
|
||||
@ -70,7 +72,7 @@ class _FieldEditorWidget extends StatelessWidget {
|
||||
(field) => ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
const FlowyText.medium("Edit property", fontSize: 12),
|
||||
FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
|
||||
const VSpace(10),
|
||||
const _FieldNameTextField(),
|
||||
const VSpace(10),
|
||||
|
@ -1,11 +1,19 @@
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart';
|
||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:window_size/window_size.dart';
|
||||
|
||||
class RowDetailPage extends StatelessWidget with FlowyOverlayDelegate {
|
||||
class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
|
||||
final GridRow rowData;
|
||||
final GridRowCache rowCache;
|
||||
|
||||
@ -16,24 +24,17 @@ class RowDetailPage extends StatelessWidget with FlowyOverlayDelegate {
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => RowDetailBloc(rowData: rowData, rowCache: rowCache),
|
||||
child: Container(),
|
||||
);
|
||||
}
|
||||
State<RowDetailPage> createState() => _RowDetailPageState();
|
||||
|
||||
void show(BuildContext context) async {
|
||||
FlowyOverlay.of(context).remove(identifier());
|
||||
|
||||
const size = Size(460, 400);
|
||||
final window = await getWindowInfo();
|
||||
final size = Size(window.frame.size.width * 0.7, window.frame.size.height * 0.7);
|
||||
FlowyOverlay.of(context).insertWithRect(
|
||||
widget: OverlayContainer(
|
||||
child: this,
|
||||
constraints: BoxConstraints.tight(const Size(460, 400)),
|
||||
constraints: BoxConstraints.tight(size),
|
||||
),
|
||||
identifier: identifier(),
|
||||
identifier: RowDetailPage.identifier(),
|
||||
anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
|
||||
anchorSize: window.frame.size,
|
||||
anchorDirection: AnchorDirection.center,
|
||||
@ -46,3 +47,97 @@ class RowDetailPage extends StatelessWidget with FlowyOverlayDelegate {
|
||||
return (RowDetailPage).toString();
|
||||
}
|
||||
}
|
||||
|
||||
class _RowDetailPageState extends State<RowDetailPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) {
|
||||
final bloc = RowDetailBloc(rowData: widget.rowData, rowCache: widget.rowCache);
|
||||
bloc.add(const RowDetailEvent.initial());
|
||||
return bloc;
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 80, vertical: 40),
|
||||
child: _PropertyList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PropertyList extends StatelessWidget {
|
||||
const _PropertyList({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RowDetailBloc, RowDetailState>(
|
||||
buildWhen: (previous, current) => previous.cellDatas != current.cellDatas,
|
||||
builder: (context, state) {
|
||||
return ListView.separated(
|
||||
itemCount: state.cellDatas.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return _RowDetailCell(cellData: state.cellDatas[index]);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const VSpace(2);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RowDetailCell extends StatelessWidget {
|
||||
final GridCell cellData;
|
||||
const _RowDetailCell({required this.cellData, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
final cell = buildGridCell(
|
||||
cellData,
|
||||
style: _buildCellStyle(theme, cellData.field.fieldType),
|
||||
);
|
||||
return SizedBox(
|
||||
height: 36,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: FieldCellButton(field: cellData.field, onTap: () {}),
|
||||
),
|
||||
const HSpace(10),
|
||||
Expanded(child: FlowyHover2(child: cell)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) {
|
||||
switch (fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return null;
|
||||
case FieldType.DateTime:
|
||||
return null;
|
||||
case FieldType.MultiSelect:
|
||||
return null;
|
||||
case FieldType.Number:
|
||||
return null;
|
||||
case FieldType.RichText:
|
||||
return GridTextCellStyle(
|
||||
hoverColor: theme.shader6,
|
||||
filled: true,
|
||||
placeholder: LocaleKeys.grid_row_textPlaceholder.tr(),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
|
||||
);
|
||||
case FieldType.SingleSelect:
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -86,29 +86,27 @@ class ActionCell<T extends ActionItem> extends StatelessWidget {
|
||||
|
||||
return FlowyHover(
|
||||
style: HoverStyle(hoverColor: theme.hover),
|
||||
builder: (context, onHover) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => onSelected(action),
|
||||
child: SizedBox(
|
||||
height: itemHeight,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (action.icon != null) action.icon!,
|
||||
HSpace(ActionListSizes.itemHPadding),
|
||||
FlowyText.medium(
|
||||
action.name,
|
||||
fontSize: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
).padding(
|
||||
horizontal: ActionListSizes.padding,
|
||||
vertical: ActionListSizes.padding,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => onSelected(action),
|
||||
child: SizedBox(
|
||||
height: itemHeight,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (action.icon != null) action.icon!,
|
||||
HSpace(ActionListSizes.itemHPadding),
|
||||
FlowyText.medium(
|
||||
action.name,
|
||||
fontSize: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
).padding(
|
||||
horizontal: ActionListSizes.padding,
|
||||
vertical: ActionListSizes.padding,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
// ignore: unused_import
|
||||
import 'package:flowy_infra/time/duration.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
typedef HoverBuilder = Widget Function(BuildContext context, bool onHover);
|
||||
|
||||
class FlowyHover extends StatefulWidget {
|
||||
final HoverStyle style;
|
||||
final HoverBuilder builder;
|
||||
final HoverBuilder? builder;
|
||||
final Widget? child;
|
||||
final bool Function()? setSelected;
|
||||
|
||||
const FlowyHover({
|
||||
Key? key,
|
||||
required this.builder,
|
||||
this.builder,
|
||||
this.child,
|
||||
required this.style,
|
||||
this.setSelected,
|
||||
}) : super(key: key);
|
||||
@ -27,25 +32,27 @@ class _FlowyHoverState extends State<FlowyHover> {
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
opaque: false,
|
||||
onEnter: (p) => setState(() => _onHover = true),
|
||||
onExit: (p) => setState(() => _onHover = false),
|
||||
child: render(),
|
||||
child: renderWidget(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget render() {
|
||||
Widget renderWidget() {
|
||||
var showHover = _onHover;
|
||||
if (!showHover && widget.setSelected != null) {
|
||||
showHover = widget.setSelected!();
|
||||
}
|
||||
|
||||
final child = widget.child ?? widget.builder!(context, _onHover);
|
||||
if (showHover) {
|
||||
return FlowyHoverContainer(
|
||||
style: widget.style,
|
||||
child: widget.builder(context, _onHover),
|
||||
child: child,
|
||||
);
|
||||
} else {
|
||||
return widget.builder(context, _onHover);
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,11 +74,11 @@ class HoverStyle {
|
||||
|
||||
class FlowyHoverContainer extends StatelessWidget {
|
||||
final HoverStyle style;
|
||||
final Widget child;
|
||||
final Widget? child;
|
||||
|
||||
const FlowyHoverContainer({
|
||||
Key? key,
|
||||
required this.child,
|
||||
this.child,
|
||||
required this.style,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -93,3 +100,104 @@ class FlowyHoverContainer extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
abstract class HoverWidget extends StatefulWidget {
|
||||
const HoverWidget({Key? key}) : super(key: key);
|
||||
|
||||
ValueNotifier<bool> get onFocus;
|
||||
}
|
||||
|
||||
class FlowyHover2 extends StatefulWidget {
|
||||
final HoverWidget child;
|
||||
const FlowyHover2({required this.child, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<FlowyHover2> createState() => _FlowyHover2State();
|
||||
}
|
||||
|
||||
class _FlowyHover2State extends State<FlowyHover2> {
|
||||
late FlowyHoverState _hoverState;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_hoverState = FlowyHoverState();
|
||||
widget.child.onFocus.addListener(() {
|
||||
_hoverState.onFocus = widget.child.onFocus.value;
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hoverState.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider.value(
|
||||
value: _hoverState,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
opaque: false,
|
||||
onEnter: (p) => setState(() => _hoverState.onHover = true),
|
||||
onExit: (p) => setState(() => _hoverState.onHover = false),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
const _HoverBackground(),
|
||||
widget.child,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HoverBackground extends StatelessWidget {
|
||||
const _HoverBackground({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.watch<AppTheme>();
|
||||
return Consumer<FlowyHoverState>(
|
||||
builder: (context, state, child) {
|
||||
if (state.onHover || state.onFocus) {
|
||||
return FlowyHoverContainer(
|
||||
style: HoverStyle(
|
||||
borderRadius: Corners.s6Border,
|
||||
hoverColor: theme.shader6,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FlowyHoverState extends ChangeNotifier {
|
||||
bool _onHover = false;
|
||||
bool _onFocus = false;
|
||||
|
||||
set onHover(bool value) {
|
||||
if (_onHover != value) {
|
||||
_onHover = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool get onHover => _onHover;
|
||||
|
||||
set onFocus(bool value) {
|
||||
if (_onFocus != value) {
|
||||
_onFocus = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool get onFocus => _onFocus;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user