瀏覽代碼

chore: database tab bar ui improvements (#3093)

* chore: tab bar ui polish

* chore: toolbar buttons ui

* chore: minor padding radius and spacing adjustment

* fix: padding issues

* chore: add view button size and padding

* chore: the carelessness is real

* fix: dark mode colors

* fix: selected icon color
Richard Shiue 1 年之前
父節點
當前提交
7cdd47757b
共有 17 個文件被更改,包括 449 次插入370 次删除
  1. 1 1
      frontend/appflowy_flutter/integration_test/util/database_test_op.dart
  2. 2 2
      frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_setting_bar.dart
  3. 7 9
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_page.dart
  4. 10 0
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/layout/sizes.dart
  5. 9 2
      frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/toolbar/calendar_setting_bar.dart
  6. 3 0
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/layout/sizes.dart
  7. 18 17
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart
  8. 16 17
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_setting_bar.dart
  9. 18 17
      frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart
  10. 13 25
      frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/setting_menu.dart
  11. 286 0
      frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_header.dart
  12. 10 235
      frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_view.dart
  13. 22 7
      frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tar_bar_add_button.dart
  14. 23 22
      frontend/appflowy_flutter/lib/plugins/database_view/widgets/setting/setting_button.dart
  15. 1 1
      frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart
  16. 3 0
      frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart
  17. 7 15
      frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart

+ 1 - 1
frontend/appflowy_flutter/integration_test/util/database_test_op.dart

@@ -31,7 +31,7 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/so
 import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_layout.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart';
-import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart';
+import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_header.dart';
 import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
 import 'package:appflowy/plugins/database_view/widgets/database_layout_ext.dart';
 import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';

+ 2 - 2
frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/toolbar/board_setting_bar.dart

@@ -12,10 +12,10 @@ class BoardSettingBar extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return SizedBox(
-      height: 40,
+      height: 20,
       child: Row(
+        mainAxisAlignment: MainAxisAlignment.end,
         children: [
-          const Spacer(),
           SettingButton(databaseController: databaseController),
         ],
       ),

+ 7 - 9
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_page.dart

@@ -1,7 +1,6 @@
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/application/database_controller.dart';
 import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
-import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
 import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart';
 import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
@@ -166,14 +165,13 @@ class _CalendarPageState extends State<CalendarPage> {
                     return const Center(
                       child: CircularProgressIndicator.adaptive(),
                     );
-                  } else {
-                    return _buildCalendar(
-                      context,
-                      _eventController,
-                      state.settings
-                          .foldLeft(0, (previous, a) => a.firstDayOfWeek),
-                    );
                   }
+                  return _buildCalendar(
+                    context,
+                    _eventController,
+                    state.settings
+                        .foldLeft(0, (previous, a) => a.firstDayOfWeek),
+                  );
                 },
               );
             },
@@ -189,7 +187,7 @@ class _CalendarPageState extends State<CalendarPage> {
     int firstDayOfWeek,
   ) {
     return Padding(
-      padding: GridSize.contentInsets,
+      padding: CalendarSize.contentInsets,
       child: LayoutBuilder(
         // must specify MonthView width for useAvailableVerticalSpace to work properly
         builder: (context, constraints) => MonthView(

+ 10 - 0
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/layout/sizes.dart

@@ -1,8 +1,18 @@
+import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
 import 'package:flutter/widgets.dart';
 
 class CalendarSize {
   static double scale = 1;
 
+  static double get headerContainerPadding => 12 * scale;
+
+  static EdgeInsets get contentInsets => EdgeInsets.fromLTRB(
+        GridSize.leadingHeaderPadding,
+        CalendarSize.headerContainerPadding,
+        GridSize.leadingHeaderPadding,
+        CalendarSize.headerContainerPadding,
+      );
+
   static double get scrollBarSize => 8 * scale;
   static double get navigatorButtonWidth => 20 * scale;
   static double get navigatorButtonHeight => 25 * scale;

+ 9 - 2
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/toolbar/calendar_setting_bar.dart

@@ -8,6 +8,7 @@ import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.da
 import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
@@ -23,11 +24,12 @@ class CalendarSettingBar extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return SizedBox(
-      height: 40,
+      height: 20,
       child: Row(
         mainAxisAlignment: MainAxisAlignment.end,
         children: [
           UnscheduleEventsButton(databaseController: databaseController),
+          const HSpace(2),
           SettingButton(
             databaseController: databaseController,
           ),
@@ -82,9 +84,14 @@ class _UnscheduleEventsButtonState extends State<UnscheduleEventsButton> {
           builder: (context, state) {
             return FlowyTextButton(
               "${LocaleKeys.calendar_settings_noDateTitle.tr()} (${state.unscheduleEvents.length})",
+              fontSize: FontSizes.s11,
+              fontColor: AFThemeExtension.of(context).textColor,
+              fontWeight: FontWeight.w400,
               fillColor: Colors.transparent,
               hoverColor: AFThemeExtension.of(context).lightGreyHover,
-              padding: GridSize.typeOptionContentInsets,
+              padding: GridSize.toolbarSettingButtonInsets,
+              radius: Corners.s4Border,
+              onPressed: () => _popoverController.show(),
             );
           },
         ),

+ 3 - 0
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/layout/sizes.dart

@@ -30,6 +30,9 @@ class GridSize {
 
   static EdgeInsets get typeOptionContentInsets => const EdgeInsets.all(4);
 
+  static EdgeInsets get toolbarSettingButtonInsets =>
+      const EdgeInsets.symmetric(horizontal: 8, vertical: 2);
+
   static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB(
         GridSize.leadingHeaderPadding,
         GridSize.headerContainerPadding,

+ 18 - 17
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart

@@ -2,6 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/grid/application/filter/filter_menu_bloc.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
@@ -30,23 +31,23 @@ class _FilterButtonState extends State<FilterButton> {
 
         return _wrapPopover(
           context,
-          SizedBox(
-            height: 26,
-            child: FlowyTextButton(
-              LocaleKeys.grid_settings_filter.tr(),
-              fontColor: textColor,
-              fillColor: Colors.transparent,
-              hoverColor: AFThemeExtension.of(context).lightGreyHover,
-              padding: GridSize.typeOptionContentInsets,
-              onPressed: () {
-                final bloc = context.read<GridFilterMenuBloc>();
-                if (bloc.state.filters.isEmpty) {
-                  _popoverController.show();
-                } else {
-                  bloc.add(const GridFilterMenuEvent.toggleMenu());
-                }
-              },
-            ),
+          FlowyTextButton(
+            LocaleKeys.grid_settings_filter.tr(),
+            fontColor: textColor,
+            fontSize: FontSizes.s11,
+            fontWeight: FontWeight.w400,
+            fillColor: Colors.transparent,
+            hoverColor: AFThemeExtension.of(context).lightGreyHover,
+            padding: GridSize.toolbarSettingButtonInsets,
+            radius: Corners.s4Border,
+            onPressed: () {
+              final bloc = context.read<GridFilterMenuBloc>();
+              if (bloc.state.filters.isEmpty) {
+                _popoverController.show();
+              } else {
+                bloc.add(const GridFilterMenuEvent.toggleMenu());
+              }
+            },
           ),
         );
       },

+ 16 - 17
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/grid_setting_bar.dart

@@ -2,8 +2,8 @@ import 'package:appflowy/plugins/database_view/application/database_controller.d
 import 'package:appflowy/plugins/database_view/grid/application/filter/filter_menu_bloc.dart';
 import 'package:appflowy/plugins/database_view/grid/application/sort/sort_menu_bloc.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
-import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
 import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
@@ -52,23 +52,22 @@ class GridSettingBar extends StatelessWidget {
           builder: (context, value, child) {
             if (value) {
               return const SizedBox.shrink();
-            } else {
-              return SizedBox(
-                height: 40,
-                child: Row(
-                  mainAxisAlignment: MainAxisAlignment.center,
-                  children: [
-                    SizedBox(width: GridSize.leadingHeaderPadding),
-                    const Spacer(),
-                    const FilterButton(),
-                    const SortButton(),
-                    SettingButton(
-                      databaseController: controller,
-                    ),
-                  ],
-                ),
-              );
             }
+            return SizedBox(
+              height: 20,
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.end,
+                children: [
+                  const FilterButton(),
+                  const HSpace(2),
+                  const SortButton(),
+                  const HSpace(2),
+                  SettingButton(
+                    databaseController: controller,
+                  ),
+                ],
+              ),
+            );
           },
         ),
       ),

+ 18 - 17
frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart

@@ -2,6 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/grid/application/sort/sort_menu_bloc.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
@@ -30,23 +31,23 @@ class _SortButtonState extends State<SortButton> {
 
         return wrapPopover(
           context,
-          SizedBox(
-            height: 26,
-            child: FlowyTextButton(
-              LocaleKeys.grid_settings_sort.tr(),
-              fontColor: textColor,
-              fillColor: Colors.transparent,
-              hoverColor: AFThemeExtension.of(context).lightGreyHover,
-              padding: GridSize.typeOptionContentInsets,
-              onPressed: () {
-                final bloc = context.read<SortMenuBloc>();
-                if (bloc.state.sortInfos.isEmpty) {
-                  _popoverController.show();
-                } else {
-                  bloc.add(const SortMenuEvent.toggleMenu());
-                }
-              },
-            ),
+          FlowyTextButton(
+            LocaleKeys.grid_settings_sort.tr(),
+            fontColor: textColor,
+            fontSize: FontSizes.s11,
+            fontWeight: FontWeight.w400,
+            fillColor: Colors.transparent,
+            hoverColor: AFThemeExtension.of(context).lightGreyHover,
+            padding: GridSize.toolbarSettingButtonInsets,
+            radius: Corners.s4Border,
+            onPressed: () {
+              final bloc = context.read<SortMenuBloc>();
+              if (bloc.state.sortInfos.isEmpty) {
+                _popoverController.show();
+              } else {
+                bloc.add(const SortMenuEvent.toggleMenu());
+              }
+            },
           ),
         );
       },

+ 13 - 25
frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/setting_menu.dart

@@ -1,7 +1,6 @@
 import 'package:appflowy/plugins/database_view/application/database_controller.dart';
 import 'package:appflowy/plugins/database_view/grid/application/grid_accessory_bloc.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
-import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -38,7 +37,7 @@ class DatabaseViewSettingExtension extends StatelessWidget {
               ),
             );
           } else {
-            return const SizedBox();
+            return const SizedBox.shrink();
           }
         },
       ),
@@ -58,29 +57,18 @@ class _DatabaseViewSettingContent extends StatelessWidget {
     return BlocBuilder<DatabaseViewSettingExtensionBloc,
         DatabaseViewSettingExtensionState>(
       builder: (context, state) {
-        final children = <Widget>[
-          Divider(
-            height: 1.0,
-            color: AFThemeExtension.of(context).toggleOffFill,
-          ),
-          const VSpace(6),
-          IntrinsicHeight(
-            child: Row(
-              children: [
-                SortMenu(
-                  fieldController: fieldController,
-                ),
-                const HSpace(6),
-                FilterMenu(
-                  fieldController: fieldController,
-                ),
-              ],
-            ),
-          )
-        ];
-
         return _wrapPadding(
-          Column(children: children),
+          Row(
+            children: [
+              SortMenu(
+                fieldController: fieldController,
+              ),
+              const HSpace(6),
+              FilterMenu(
+                fieldController: fieldController,
+              ),
+            ],
+          ),
         );
       },
     );
@@ -90,7 +78,7 @@ class _DatabaseViewSettingContent extends StatelessWidget {
     return Padding(
       padding: EdgeInsets.symmetric(
         horizontal: GridSize.leadingHeaderPadding,
-        vertical: 6,
+        vertical: 8,
       ),
       child: child,
     );

+ 286 - 0
frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_header.dart

@@ -0,0 +1,286 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/workspace/application/view/view_ext.dart';
+import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
+import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
+import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/image.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_infra/theme_extension.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import '../application/tar_bar_bloc.dart';
+import 'tar_bar_add_button.dart';
+
+class TabBarHeader extends StatefulWidget {
+  const TabBarHeader({super.key});
+
+  @override
+  State<TabBarHeader> createState() => _TabBarHeaderState();
+}
+
+class _TabBarHeaderState extends State<TabBarHeader> {
+  @override
+  Widget build(BuildContext context) {
+    return Stack(
+      children: [
+        Positioned(
+          bottom: 0,
+          left: 0,
+          right: 0,
+          child: Divider(
+            color: Theme.of(context).dividerColor,
+            height: 1,
+            thickness: 1,
+          ),
+        ),
+        Row(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            BlocBuilder<GridTabBarBloc, GridTabBarState>(
+              builder: (context, state) {
+                return const Flexible(
+                  child: DatabaseTabBar(),
+                );
+              },
+            ),
+            BlocBuilder<GridTabBarBloc, GridTabBarState>(
+              builder: (context, state) {
+                return SizedBox(
+                  width: 200,
+                  child: Column(
+                    children: [
+                      const VSpace(3),
+                      pageSettingBarFromState(state),
+                    ],
+                  ),
+                );
+              },
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+
+  Widget pageSettingBarFromState(GridTabBarState state) {
+    if (state.tabBars.length < state.selectedIndex) {
+      return const SizedBox.shrink();
+    }
+    final tarBar = state.tabBars[state.selectedIndex];
+    final controller =
+        state.tabBarControllerByViewId[tarBar.viewId]!.controller;
+    return tarBar.builder.settingBar(
+      context,
+      controller,
+    );
+  }
+}
+
+class DatabaseTabBar extends StatefulWidget {
+  const DatabaseTabBar({super.key});
+
+  @override
+  State<DatabaseTabBar> createState() => _DatabaseTabBarState();
+}
+
+class _DatabaseTabBarState extends State<DatabaseTabBar> {
+  final _scrollController = ScrollController();
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GridTabBarBloc, GridTabBarState>(
+      builder: (context, state) {
+        final children = state.tabBars.indexed.map((indexed) {
+          final isSelected = state.selectedIndex == indexed.$1;
+          final tabBar = indexed.$2;
+          return DatabaseTabBarItem(
+            key: ValueKey(tabBar.viewId),
+            view: tabBar.view,
+            isSelected: isSelected,
+            onTap: (selectedView) {
+              context.read<GridTabBarBloc>().add(
+                    GridTabBarEvent.selectView(selectedView.id),
+                  );
+            },
+          );
+        }).toList();
+
+        return Row(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Flexible(
+              child: ListView(
+                controller: _scrollController,
+                scrollDirection: Axis.horizontal,
+                shrinkWrap: true,
+                children: children,
+              ),
+            ),
+            AddDatabaseViewButton(
+              onTap: (action) async {
+                context.read<GridTabBarBloc>().add(
+                      GridTabBarEvent.createView(action),
+                    );
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+}
+
+class DatabaseTabBarItem extends StatelessWidget {
+  final bool isSelected;
+  final ViewPB view;
+  final Function(ViewPB) onTap;
+  const DatabaseTabBarItem({
+    required this.view,
+    required this.isSelected,
+    required this.onTap,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return ConstrainedBox(
+      constraints: const BoxConstraints(maxWidth: 160),
+      child: Stack(
+        children: [
+          SizedBox(
+            height: 26,
+            child: TabBarItemButton(
+              view: view,
+              isSelected: isSelected,
+              onTap: () => onTap(view),
+            ),
+          ),
+          if (isSelected)
+            Positioned(
+              bottom: 0,
+              left: 0,
+              right: 0,
+              child: Divider(
+                height: 2,
+                thickness: 2,
+                color: Theme.of(context).colorScheme.primary,
+              ),
+            ),
+        ],
+      ),
+    );
+  }
+}
+
+class TabBarItemButton extends StatelessWidget {
+  final ViewPB view;
+  final bool isSelected;
+  final VoidCallback onTap;
+  const TabBarItemButton({
+    required this.view,
+    required this.onTap,
+    super.key,
+    required this.isSelected,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return PopoverActionList<TabBarViewAction>(
+      direction: PopoverDirection.bottomWithCenterAligned,
+      actions: TabBarViewAction.values,
+      buildChild: (controller) {
+        Color? color;
+        if (!isSelected) {
+          color = Theme.of(context).hintColor;
+        }
+        if (Theme.of(context).brightness == Brightness.dark) {
+          color = null;
+        }
+        return IntrinsicWidth(
+          child: FlowyButton(
+            radius: Corners.s6Border,
+            hoverColor: AFThemeExtension.of(context).greyHover,
+            onTap: onTap,
+            onSecondaryTap: () {
+              controller.show();
+            },
+            leftIcon: FlowySvg(
+              name: view.iconName,
+              size: const Size(14, 14),
+              color: color,
+            ),
+            text: FlowyText(
+              view.name,
+              fontSize: FontSizes.s11,
+              textAlign: TextAlign.center,
+              overflow: TextOverflow.ellipsis,
+              color: color,
+              fontWeight: isSelected ? null : FontWeight.w400,
+            ),
+          ),
+        );
+      },
+      onSelected: (action, controller) {
+        switch (action) {
+          case TabBarViewAction.rename:
+            NavigatorTextFieldDialog(
+              title: LocaleKeys.menuAppHeader_renameDialog.tr(),
+              value: view.name,
+              confirm: (newValue) {
+                context.read<GridTabBarBloc>().add(
+                      GridTabBarEvent.renameView(view.id, newValue),
+                    );
+              },
+            ).show(context);
+            break;
+          case TabBarViewAction.delete:
+            NavigatorAlertDialog(
+              title: LocaleKeys.grid_deleteView.tr(),
+              confirm: () {
+                context.read<GridTabBarBloc>().add(
+                      GridTabBarEvent.deleteView(view.id),
+                    );
+              },
+            ).show(context);
+
+            break;
+        }
+        controller.close();
+      },
+    );
+  }
+}
+
+enum TabBarViewAction implements ActionCell {
+  rename,
+  delete;
+
+  @override
+  String get name {
+    switch (this) {
+      case TabBarViewAction.rename:
+        return LocaleKeys.disclosureAction_rename.tr();
+      case TabBarViewAction.delete:
+        return LocaleKeys.disclosureAction_delete.tr();
+    }
+  }
+
+  Widget icon(Color iconColor) {
+    switch (this) {
+      case TabBarViewAction.rename:
+        return const FlowySvg(name: 'editor/edit');
+      case TabBarViewAction.delete:
+        return const FlowySvg(name: 'editor/delete');
+    }
+  }
+
+  @override
+  Widget? leftIcon(Color iconColor) => icon(iconColor);
+
+  @override
+  Widget? rightIcon(Color iconColor) => null;
+}

+ 10 - 235
frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tab_bar_view.dart

@@ -1,26 +1,16 @@
-import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/application/tar_bar_bloc.dart';
 import 'package:appflowy/plugins/util.dart';
 import 'package:appflowy/startup/plugin/plugin.dart';
-import 'package:appflowy/workspace/application/view/view_ext.dart';
 import 'package:appflowy/workspace/presentation/home/home_stack.dart';
-import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
 import 'package:appflowy/workspace/presentation/widgets/left_bar_item.dart';
-import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
 import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart';
 import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
-import 'package:appflowy_popover/appflowy_popover.dart';
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flowy_infra/image.dart';
-import 'package:flowy_infra/size.dart';
-import 'package:flowy_infra/theme_extension.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import '../application/database_controller.dart';
 import '../grid/presentation/layout/sizes.dart';
-import 'tar_bar_add_button.dart';
+import 'tab_bar_header.dart';
 
 abstract class DatabaseTabBarItemBuilder {
   const DatabaseTabBarItemBuilder();
@@ -103,33 +93,16 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
                   builder: (_, value, ___) {
                     if (value) {
                       return const SizedBox.shrink();
-                    } else {
-                      return Row(
-                        children: [
-                          BlocBuilder<GridTabBarBloc, GridTabBarState>(
-                            builder: (context, state) {
-                              return const Flexible(
-                                child: Padding(
-                                  padding: EdgeInsets.only(left: 50),
-                                  child: DatabaseTabBar(),
-                                ),
-                              );
-                            },
-                          ),
-                          BlocBuilder<GridTabBarBloc, GridTabBarState>(
-                            builder: (context, state) {
-                              return SizedBox(
-                                width: 300,
-                                child: Padding(
-                                  padding: const EdgeInsets.only(right: 50),
-                                  child: pageSettingBarFromState(state),
-                                ),
-                              );
-                            },
-                          ),
-                        ],
-                      );
                     }
+                    return SizedBox(
+                      height: 30,
+                      child: Padding(
+                        padding: EdgeInsets.symmetric(
+                          horizontal: GridSize.leadingHeaderPadding,
+                        ),
+                        child: const TabBarHeader(),
+                      ),
+                    );
                   },
                 );
               },
@@ -170,19 +143,6 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
     }).toList();
   }
 
-  Widget pageSettingBarFromState(GridTabBarState state) {
-    if (state.tabBars.length < state.selectedIndex) {
-      return const SizedBox.shrink();
-    }
-    final tarBar = state.tabBars[state.selectedIndex];
-    final controller =
-        state.tabBarControllerByViewId[tarBar.viewId]!.controller;
-    return tarBar.builder.settingBar(
-      context,
-      controller,
-    );
-  }
-
   Widget pageSettingBarExtensionFromState(GridTabBarState state) {
     if (state.tabBars.length < state.selectedIndex) {
       return const SizedBox.shrink();
@@ -253,188 +213,3 @@ class DatabasePluginWidgetBuilder extends PluginWidgetBuilder {
   @override
   List<NavigationItem> get navigationItems => [this];
 }
-
-class DatabaseTabBar extends StatefulWidget {
-  const DatabaseTabBar({super.key});
-
-  @override
-  State<DatabaseTabBar> createState() => _DatabaseTabBarState();
-}
-
-class _DatabaseTabBarState extends State<DatabaseTabBar> {
-  final _scrollController = ScrollController();
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<GridTabBarBloc, GridTabBarState>(
-      builder: (context, state) {
-        final children = state.tabBars.indexed.map((indexed) {
-          final isSelected = state.selectedIndex == indexed.$1;
-          final tabBar = indexed.$2;
-          return DatabaseTabBarItem(
-            key: ValueKey(tabBar.viewId),
-            view: tabBar.view,
-            isSelected: isSelected,
-            onTap: (selectedView) {
-              context.read<GridTabBarBloc>().add(
-                    GridTabBarEvent.selectView(selectedView.id),
-                  );
-            },
-          );
-        }).toList();
-
-        return Row(
-          children: [
-            Flexible(
-              child: SingleChildScrollView(
-                controller: _scrollController,
-                scrollDirection: Axis.horizontal,
-                child: IntrinsicWidth(
-                  child: Row(children: children),
-                ),
-              ),
-            ),
-            AddDatabaseViewButton(
-              onTap: (action) async {
-                context.read<GridTabBarBloc>().add(
-                      GridTabBarEvent.createView(action),
-                    );
-              },
-            ),
-          ],
-        );
-      },
-    );
-  }
-}
-
-class DatabaseTabBarItem extends StatelessWidget {
-  final bool isSelected;
-  final ViewPB view;
-  final Function(ViewPB) onTap;
-  const DatabaseTabBarItem({
-    required this.view,
-    required this.isSelected,
-    required this.onTap,
-    super.key,
-  });
-
-  @override
-  Widget build(BuildContext context) {
-    return ConstrainedBox(
-      constraints: const BoxConstraints(minWidth: 80, maxWidth: 160),
-      child: IntrinsicWidth(
-        child: Column(
-          children: [
-            TabBarItemButton(
-              view: view,
-              onTap: () => onTap(view),
-            ),
-            if (isSelected)
-              Divider(
-                height: 1,
-                thickness: 2,
-                color: Theme.of(context).colorScheme.secondary,
-              ),
-          ],
-        ),
-      ),
-    );
-  }
-}
-
-class TabBarItemButton extends StatelessWidget {
-  final ViewPB view;
-  final VoidCallback onTap;
-  const TabBarItemButton({
-    required this.view,
-    required this.onTap,
-    super.key,
-  });
-
-  @override
-  Widget build(BuildContext context) {
-    return PopoverActionList<TabBarViewAction>(
-      direction: PopoverDirection.bottomWithCenterAligned,
-      actions: TabBarViewAction.values,
-      buildChild: (controller) {
-        return FlowyButton(
-          radius: Corners.s5Border,
-          hoverColor: AFThemeExtension.of(context).greyHover,
-          onTap: onTap,
-          onSecondaryTap: () {
-            controller.show();
-          },
-          text: FlowyText.medium(
-            view.name,
-            maxLines: 1,
-            textAlign: TextAlign.center,
-            overflow: TextOverflow.ellipsis,
-          ),
-          margin: GridSize.cellContentInsets,
-          leftIcon: svgWidget(
-            view.iconName,
-            color: Theme.of(context).iconTheme.color,
-          ),
-        );
-      },
-      onSelected: (action, controller) {
-        switch (action) {
-          case TabBarViewAction.rename:
-            NavigatorTextFieldDialog(
-              title: LocaleKeys.menuAppHeader_renameDialog.tr(),
-              value: view.name,
-              confirm: (newValue) {
-                context.read<GridTabBarBloc>().add(
-                      GridTabBarEvent.renameView(view.id, newValue),
-                    );
-              },
-            ).show(context);
-            break;
-          case TabBarViewAction.delete:
-            NavigatorAlertDialog(
-              title: LocaleKeys.grid_deleteView.tr(),
-              confirm: () {
-                context.read<GridTabBarBloc>().add(
-                      GridTabBarEvent.deleteView(view.id),
-                    );
-              },
-            ).show(context);
-
-            break;
-        }
-        controller.close();
-      },
-    );
-  }
-}
-
-enum TabBarViewAction implements ActionCell {
-  rename,
-  delete;
-
-  @override
-  String get name {
-    switch (this) {
-      case TabBarViewAction.rename:
-        return LocaleKeys.disclosureAction_rename.tr();
-      case TabBarViewAction.delete:
-        return LocaleKeys.disclosureAction_delete.tr();
-    }
-  }
-
-  Widget icon(Color iconColor) {
-    switch (this) {
-      case TabBarViewAction.rename:
-        return const FlowySvg(name: 'editor/edit');
-      case TabBarViewAction.delete:
-        return const FlowySvg(name: 'editor/delete');
-    }
-  }
-
-  @override
-  Widget? leftIcon(Color iconColor) => icon(iconColor);
-
-  @override
-  Widget? rightIcon(Color iconColor) => null;
-}

+ 22 - 7
frontend/appflowy_flutter/lib/plugins/database_view/tar_bar/tar_bar_add_button.dart

@@ -4,6 +4,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
+import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/extension.dart';
@@ -32,13 +33,27 @@ class _AddDatabaseViewButtonState extends State<AddDatabaseViewButton> {
       offset: const Offset(0, 8),
       margin: EdgeInsets.zero,
       triggerActions: PopoverTriggerFlags.none,
-      child: FlowyIconButton(
-        iconPadding: const EdgeInsets.all(4),
-        hoverColor: AFThemeExtension.of(context).greyHover,
-        onPressed: () => popoverController.show(),
-        icon: svgWidget(
-          'home/add',
-          color: Theme.of(context).colorScheme.tertiary,
+      child: SizedBox(
+        height: 26,
+        child: Row(
+          children: [
+            VerticalDivider(
+              width: 1.0,
+              thickness: 1.0,
+              indent: 4.0,
+              endIndent: 4.0,
+              color: Theme.of(context).dividerColor,
+            ),
+            FlowyIconButton(
+              width: 26,
+              iconPadding: const EdgeInsets.all(5),
+              hoverColor: AFThemeExtension.of(context).greyHover,
+              onPressed: () => popoverController.show(),
+              radius: Corners.s4Border,
+              icon: const FlowySvg(name: 'home/add'),
+              iconColorOnHover: Theme.of(context).colorScheme.onSurface,
+            ),
+          ],
         ),
       ),
       popupBuilder: (BuildContext context) {

+ 23 - 22
frontend/appflowy_flutter/lib/plugins/database_view/widgets/setting/setting_button.dart

@@ -6,6 +6,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.d
 import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme_extension.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
@@ -32,29 +33,29 @@ class _SettingButtonState extends State<SettingButton> {
 
   @override
   Widget build(BuildContext context) {
-    return SizedBox(
-      height: 26,
-      child: AppFlowyPopover(
-        controller: _popoverController,
-        constraints: BoxConstraints.loose(const Size(200, 400)),
-        direction: PopoverDirection.bottomWithLeftAligned,
-        offset: const Offset(0, 8),
-        margin: EdgeInsets.zero,
-        triggerActions: PopoverTriggerFlags.none,
-        child: FlowyTextButton(
-          LocaleKeys.settings_title.tr(),
-          fontColor: AFThemeExtension.of(context).textColor,
-          fillColor: Colors.transparent,
-          hoverColor: AFThemeExtension.of(context).lightGreyHover,
-          padding: GridSize.typeOptionContentInsets,
-          onPressed: () => _popoverController.show(),
-        ),
-        popupBuilder: (BuildContext context) {
-          return _DatabaseSettingListPopover(
-            databaseController: widget.databaseController,
-          );
-        },
+    return AppFlowyPopover(
+      controller: _popoverController,
+      constraints: BoxConstraints.loose(const Size(200, 400)),
+      direction: PopoverDirection.bottomWithCenterAligned,
+      offset: const Offset(0, 8),
+      margin: EdgeInsets.zero,
+      triggerActions: PopoverTriggerFlags.none,
+      child: FlowyTextButton(
+        LocaleKeys.settings_title.tr(),
+        fontColor: AFThemeExtension.of(context).textColor,
+        fontSize: FontSizes.s11,
+        fontWeight: FontWeight.w400,
+        fillColor: Colors.transparent,
+        hoverColor: AFThemeExtension.of(context).lightGreyHover,
+        padding: GridSize.toolbarSettingButtonInsets,
+        radius: Corners.s4Border,
+        onPressed: () => _popoverController.show(),
       ),
+      popupBuilder: (BuildContext context) {
+        return _DatabaseSettingListPopover(
+          databaseController: widget.databaseController,
+        );
+      },
     );
   }
 }

+ 1 - 1
frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart

@@ -118,7 +118,7 @@ extension ViewLayoutExtension on ViewLayoutPB {
       case ViewLayoutPB.Board:
         return 'editor/board';
       case ViewLayoutPB.Calendar:
-        return 'editor/calendar';
+        return 'editor/date';
       case ViewLayoutPB.Document:
         return 'editor/documents';
       default:

+ 3 - 0
frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart

@@ -65,6 +65,9 @@ class Corners {
   static const BorderRadius s3Border = BorderRadius.all(s3Radius);
   static const Radius s3Radius = Radius.circular(3);
 
+  static const BorderRadius s4Border = BorderRadius.all(s4Radius);
+  static const Radius s4Radius = Radius.circular(4);
+
   static const BorderRadius s5Border = BorderRadius.all(s5Radius);
   static const Radius s5Radius = Radius.circular(5);
 

+ 7 - 15
frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart

@@ -148,7 +148,7 @@ class FlowyTextButton extends StatelessWidget {
     this.radius,
     this.mainAxisAlignment = MainAxisAlignment.start,
     this.tooltip,
-    this.constraints = const BoxConstraints(minWidth: 58.0, minHeight: 30.0),
+    this.constraints = const BoxConstraints(minWidth: 0.0, minHeight: 0.0),
     this.decoration,
     this.fontFamily,
   }) : super(key: key);
@@ -173,17 +173,13 @@ class FlowyTextButton extends StatelessWidget {
       ),
     );
 
-    Widget child = Padding(
-      padding: padding,
-      child: Row(
-        crossAxisAlignment: CrossAxisAlignment.center,
-        mainAxisAlignment: mainAxisAlignment,
-        children: children,
-      ),
+    Widget child = Row(
+      crossAxisAlignment: CrossAxisAlignment.center,
+      mainAxisAlignment: mainAxisAlignment,
+      children: children,
     );
 
     child = RawMaterialButton(
-      visualDensity: VisualDensity.compact,
       hoverElevation: 0,
       highlightElevation: 0,
       shape: RoundedRectangleBorder(borderRadius: radius ?? Corners.s6Border),
@@ -196,12 +192,8 @@ class FlowyTextButton extends StatelessWidget {
       elevation: 0,
       constraints: constraints,
       materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
-      onPressed: () {},
-      child: child,
-    );
-
-    child = IgnoreParentGestureWidget(
-      onPress: onPressed,
+      padding: padding,
+      onPressed: onPressed,
       child: child,
     );