mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: refactor space icon picker (#5878)
* feat: refactor space icon picker * chore: optimize the _loadIconGroups function * feat: refactor emoji picker * feat: integrate icon picker into flowy_icon_emoji_picker * feat: support searching icon * feat: support displaying new icons * fix: flutter analyze * chore: join lines * feat: support space icon in view title * feat: support customzing icon when creating space or managing space * feat: customize the emoji picker and icon picker padding * feat: shuffle icon * fix: expand popup menu font size * fix: flutter integration test
This commit is contained in:
parent
4041724980
commit
453e6309d5
1
frontend/appflowy_flutter/assets/icons/icons.json
Normal file
1
frontend/appflowy_flutter/assets/icons/icons.json
Normal file
File diff suppressed because one or more lines are too long
@ -1,13 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_add_button.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_add_button.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart';
|
||||||
@ -15,8 +11,11 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/header/doc
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
|
||||||
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ class EditorOperations {
|
|||||||
final Finder button = !isInPicker
|
final Finder button = !isInPicker
|
||||||
? find.text(LocaleKeys.document_plugins_cover_removeIcon.tr())
|
? find.text(LocaleKeys.document_plugins_cover_removeIcon.tr())
|
||||||
: find.descendant(
|
: find.descendant(
|
||||||
of: find.byType(FlowyIconPicker),
|
of: find.byType(FlowyIconEmojiPicker),
|
||||||
matching: find.text(LocaleKeys.button_remove.tr()),
|
matching: find.text(LocaleKeys.button_remove.tr()),
|
||||||
);
|
);
|
||||||
await tester.tapButton(button);
|
await tester.tapButton(button);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||||
@ -58,7 +58,7 @@ extension AppFlowyWorkspace on WidgetTester {
|
|||||||
);
|
);
|
||||||
expect(iconButton, findsOneWidget);
|
expect(iconButton, findsOneWidget);
|
||||||
await tapButton(iconButton);
|
await tapButton(iconButton);
|
||||||
final iconPicker = find.byType(FlowyIconPicker);
|
final iconPicker = find.byType(FlowyIconEmojiPicker);
|
||||||
expect(iconPicker, findsOneWidget);
|
expect(iconPicker, findsOneWidget);
|
||||||
await tapButton(find.findTextInFlowyText(icon));
|
await tapButton(find.findTextInFlowyText(icon));
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
|||||||
import 'package:appflowy/mobile/presentation/home/mobile_home_setting_page.dart';
|
import 'package:appflowy/mobile/presentation/home/mobile_home_setting_page.dart';
|
||||||
import 'package:appflowy/mobile/presentation/home/workspaces/workspace_menu_bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/home/workspaces/workspace_menu_bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/util/built_in_svgs.dart';
|
import 'package:appflowy/util/built_in_svgs.dart';
|
||||||
import 'package:appflowy/workspace/application/user/settings_user_bloc.dart';
|
import 'package:appflowy/workspace/application/user/settings_user_bloc.dart';
|
||||||
@ -133,6 +133,7 @@ class _MobileWorkspace extends StatelessWidget {
|
|||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
enableEdit: false,
|
enableEdit: false,
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
|
figmaLineHeight: 16.0,
|
||||||
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
||||||
UserWorkspaceEvent.updateWorkspaceIcon(
|
UserWorkspaceEvent.updateWorkspaceIcon(
|
||||||
currentWorkspace.workspaceId,
|
currentWorkspace.workspaceId,
|
||||||
|
@ -144,6 +144,7 @@ class _WorkspaceMenuItem extends StatelessWidget {
|
|||||||
enableEdit: false,
|
enableEdit: false,
|
||||||
iconSize: 26,
|
iconSize: 26,
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
|
figmaLineHeight: 16.0,
|
||||||
workspace: workspace,
|
workspace: workspace,
|
||||||
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
||||||
UserWorkspaceEvent.updateWorkspaceIcon(
|
UserWorkspaceEvent.updateWorkspaceIcon(
|
||||||
|
@ -68,17 +68,16 @@ class MobileNotificationTabBar extends StatelessWidget {
|
|||||||
controller: tabController,
|
controller: tabController,
|
||||||
tabs: tabs.map((e) => Tab(text: e.tr)).toList(),
|
tabs: tabs.map((e) => Tab(text: e.tr)).toList(),
|
||||||
indicatorSize: TabBarIndicatorSize.label,
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
indicatorColor: Theme.of(context).primaryColor,
|
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
labelStyle: labelStyle,
|
labelStyle: labelStyle,
|
||||||
labelColor: baseStyle?.color,
|
labelColor: baseStyle?.color,
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
labelPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
unselectedLabelStyle: unselectedLabelStyle,
|
unselectedLabelStyle: unselectedLabelStyle,
|
||||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
indicator: RoundUnderlineTabIndicator(
|
indicator: const RoundUnderlineTabIndicator(
|
||||||
width: 28.0,
|
width: 28.0,
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Theme.of(context).primaryColor,
|
color: Color(0xFF00C8FF),
|
||||||
width: 3,
|
width: 3,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_header.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker_header.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_search_bar.dart';
|
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/emoji_search_bar.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||||
|
|
||||||
// use a global value to store the selected emoji to prevent reloading every time.
|
// use a global value to store the selected emoji to prevent reloading every time.
|
||||||
@ -65,28 +64,34 @@ class _FlowyEmojiPickerState extends State<FlowyEmojiPicker> {
|
|||||||
perLine: widget.emojiPerLine,
|
perLine: widget.emojiPerLine,
|
||||||
),
|
),
|
||||||
onEmojiSelected: widget.onEmojiSelected,
|
onEmojiSelected: widget.onEmojiSelected,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
headerBuilder: (context, category) {
|
headerBuilder: (context, category) {
|
||||||
return FlowyEmojiHeader(
|
return FlowyEmojiHeader(
|
||||||
category: category,
|
category: category,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemBuilder: (context, emojiId, emoji, callback) {
|
itemBuilder: (context, emojiId, emoji, callback) {
|
||||||
return SizedBox(
|
final name = emojiData?.emojis[emojiId]?.name ?? '';
|
||||||
width: 36,
|
return SizedBox.square(
|
||||||
height: 36,
|
dimension: 36.0,
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
radius: Corners.s8Border,
|
radius: Corners.s8Border,
|
||||||
text: FlowyText.emoji(
|
text: FlowyTooltip(
|
||||||
|
message: name,
|
||||||
|
child: FlowyText.emoji(
|
||||||
emoji,
|
emoji,
|
||||||
fontSize: 24.0,
|
fontSize: 24.0,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
onTap: () => callback(emojiId, emoji),
|
onTap: () => callback(emojiId, emoji),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
searchBarBuilder: (context, keyword, skinTone) {
|
searchBarBuilder: (context, keyword, skinTone) {
|
||||||
return FlowyEmojiSearchBar(
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: FlowyEmojiSearchBar(
|
||||||
emojiData: emojiData!,
|
emojiData: emojiData!,
|
||||||
onKeywordChanged: (value) {
|
onKeywordChanged: (value) {
|
||||||
keyword.value = value;
|
keyword.value = value;
|
||||||
@ -95,6 +100,7 @@ class _FlowyEmojiPickerState extends State<FlowyEmojiPicker> {
|
|||||||
skinTone.value = value;
|
skinTone.value = value;
|
||||||
},
|
},
|
||||||
onRandomEmojiSelected: widget.onEmojiSelected,
|
onRandomEmojiSelected: widget.onEmojiSelected,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker_page.dart';
|
import 'package:appflowy/plugins/base/icon/icon_picker_page.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||||
|
|
||||||
|
@ -1,111 +0,0 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/icon.pbenum.dart';
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
extension ToProto on FlowyIconType {
|
|
||||||
ViewIconTypePB toProto() {
|
|
||||||
switch (this) {
|
|
||||||
case FlowyIconType.emoji:
|
|
||||||
return ViewIconTypePB.Emoji;
|
|
||||||
case FlowyIconType.icon:
|
|
||||||
return ViewIconTypePB.Icon;
|
|
||||||
case FlowyIconType.custom:
|
|
||||||
return ViewIconTypePB.Url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FlowyIconType {
|
|
||||||
emoji,
|
|
||||||
icon,
|
|
||||||
custom;
|
|
||||||
}
|
|
||||||
|
|
||||||
class EmojiPickerResult {
|
|
||||||
factory EmojiPickerResult.none() =>
|
|
||||||
const EmojiPickerResult(FlowyIconType.icon, '');
|
|
||||||
|
|
||||||
factory EmojiPickerResult.emoji(String emoji) =>
|
|
||||||
EmojiPickerResult(FlowyIconType.emoji, emoji);
|
|
||||||
|
|
||||||
const EmojiPickerResult(
|
|
||||||
this.type,
|
|
||||||
this.emoji,
|
|
||||||
);
|
|
||||||
|
|
||||||
final FlowyIconType type;
|
|
||||||
final String emoji;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FlowyIconPicker extends StatelessWidget {
|
|
||||||
const FlowyIconPicker({
|
|
||||||
super.key,
|
|
||||||
required this.onSelected,
|
|
||||||
});
|
|
||||||
|
|
||||||
final void Function(EmojiPickerResult result) onSelected;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const VSpace(8.0),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
FlowyText(LocaleKeys.newSettings_workplace_chooseAnIcon.tr()),
|
|
||||||
const Spacer(),
|
|
||||||
_RemoveIconButton(
|
|
||||||
onTap: () => onSelected(EmojiPickerResult.none()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const VSpace(12.0),
|
|
||||||
const Divider(height: 0.5),
|
|
||||||
Expanded(
|
|
||||||
child: FlowyEmojiPicker(
|
|
||||||
emojiPerLine: _getEmojiPerLine(context),
|
|
||||||
onEmojiSelected: (_, emoji) =>
|
|
||||||
onSelected(EmojiPickerResult.emoji(emoji)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _getEmojiPerLine(BuildContext context) {
|
|
||||||
if (PlatformExtension.isDesktopOrWeb) {
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
final width = MediaQuery.of(context).size.width;
|
|
||||||
return width ~/ 40.0; // the size of the emoji
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RemoveIconButton extends StatelessWidget {
|
|
||||||
const _RemoveIconButton({required this.onTap});
|
|
||||||
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox(
|
|
||||||
height: 24,
|
|
||||||
child: FlowyButton(
|
|
||||||
onTap: onTap,
|
|
||||||
useIntrinsicWidth: true,
|
|
||||||
text: FlowyText.regular(
|
|
||||||
LocaleKeys.button_remove.tr(),
|
|
||||||
color: Theme.of(context).hintColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
|
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class IconPickerPage extends StatelessWidget {
|
class IconPickerPage extends StatelessWidget {
|
||||||
const IconPickerPage({
|
const IconPickerPage({
|
||||||
@ -22,7 +21,7 @@ class IconPickerPage extends StatelessWidget {
|
|||||||
titleText: title ?? LocaleKeys.titleBar_pageIcon.tr(),
|
titleText: title ?? LocaleKeys.titleBar_pageIcon.tr(),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: FlowyIconPicker(onSelected: onSelected),
|
child: FlowyIconEmojiPicker(onSelectedEmoji: onSelected),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import 'package:flowy_infra/theme_extension.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart' hide Card;
|
import 'package:flutter/material.dart' hide Card;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -9,7 +9,6 @@ import 'package:appflowy_board/appflowy_board.dart';
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -17,7 +17,6 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class CalendarEventEditor extends StatelessWidget {
|
class CalendarEventEditor extends StatelessWidget {
|
||||||
|
@ -20,7 +20,6 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import 'package:appflowy_backend/protobuf/flowy-database2/number_entities.pb.dar
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import 'package:appflowy/plugins/database/application/database_controller.dart';
|
|||||||
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -7,7 +7,6 @@ import 'package:appflowy/plugins/database/widgets/cell_editor/date_editor.dart';
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import '../editable_cell_skeleton/date.dart';
|
import '../editable_cell_skeleton/date.dart';
|
||||||
|
@ -8,7 +8,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import 'package:appflowy/plugins/database/widgets/cell_editor/date_editor.dart';
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class DesktopRowDetailDateCellSkin extends IEditableDateCellSkin {
|
class DesktopRowDetailDateCellSkin extends IEditableDateCellSkin {
|
||||||
|
@ -13,7 +13,6 @@ import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/em
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
|||||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:avatar_stack/avatar_stack.dart';
|
import 'package:avatar_stack/avatar_stack.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:string_validator/string_validator.dart';
|
import 'package:string_validator/string_validator.dart';
|
||||||
|
@ -4,7 +4,6 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
const String leftAlignmentKey = 'left';
|
const String leftAlignmentKey = 'left';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class MenuBlockButton extends StatelessWidget {
|
class MenuBlockButton extends StatelessWidget {
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
|
||||||
import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/desktop_cover.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/desktop_cover.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||||
@ -17,6 +14,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/uplo
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||||
import 'package:appflowy/shared/appflowy_network_image.dart';
|
import 'package:appflowy/shared/appflowy_network_image.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
@ -25,6 +23,7 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:string_validator/string_validator.dart';
|
import 'package:string_validator/string_validator.dart';
|
||||||
@ -352,11 +351,12 @@ class _DocumentHeaderToolbarState extends State<DocumentHeaderToolbar> {
|
|||||||
offset: const Offset(0, 8),
|
offset: const Offset(0, 8),
|
||||||
direction: PopoverDirection.bottomWithCenterAligned,
|
direction: PopoverDirection.bottomWithCenterAligned,
|
||||||
constraints: BoxConstraints.loose(const Size(360, 380)),
|
constraints: BoxConstraints.loose(const Size(360, 380)),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
child: child,
|
child: child,
|
||||||
popupBuilder: (BuildContext popoverContext) {
|
popupBuilder: (BuildContext popoverContext) {
|
||||||
isPopoverOpen = true;
|
isPopoverOpen = true;
|
||||||
return FlowyIconPicker(
|
return FlowyIconEmojiPicker(
|
||||||
onSelected: (result) {
|
onSelectedEmoji: (result) {
|
||||||
widget.onIconOrCoverChanged(icon: result.emoji);
|
widget.onIconOrCoverChanged(icon: result.emoji);
|
||||||
_popoverController.close();
|
_popoverController.close();
|
||||||
},
|
},
|
||||||
@ -725,10 +725,11 @@ class _DocumentIconState extends State<DocumentIcon> {
|
|||||||
controller: _popoverController,
|
controller: _popoverController,
|
||||||
offset: const Offset(0, 8),
|
offset: const Offset(0, 8),
|
||||||
constraints: BoxConstraints.loose(const Size(360, 380)),
|
constraints: BoxConstraints.loose(const Size(360, 380)),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
child: child,
|
child: child,
|
||||||
popupBuilder: (BuildContext popoverContext) {
|
popupBuilder: (BuildContext popoverContext) {
|
||||||
return FlowyIconPicker(
|
return FlowyIconEmojiPicker(
|
||||||
onSelected: (result) {
|
onSelectedEmoji: (result) {
|
||||||
widget.onChangeIcon(result.emoji);
|
widget.onChangeIcon(result.emoji);
|
||||||
_popoverController.close();
|
_popoverController.close();
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,6 @@ import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -4,7 +4,6 @@ import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
|||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||||
|
|
||||||
@ -142,6 +141,7 @@ class _SearchTextFieldState extends State<_SearchTextField> {
|
|||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Theme.of(context).hintColor,
|
color: Theme.of(context).hintColor,
|
||||||
),
|
),
|
||||||
|
enableBorderColor: const Color(0x1E171717),
|
||||||
controller: controller,
|
controller: controller,
|
||||||
onChanged: widget.onKeywordChanged,
|
onChanged: widget.onKeywordChanged,
|
||||||
prefixIcon: const Padding(
|
prefixIcon: const Padding(
|
@ -0,0 +1,180 @@
|
|||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/tab.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-folder/icon.pbenum.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart' hide Icon;
|
||||||
|
|
||||||
|
import 'icon.dart';
|
||||||
|
|
||||||
|
extension ToProto on FlowyIconType {
|
||||||
|
ViewIconTypePB toProto() {
|
||||||
|
switch (this) {
|
||||||
|
case FlowyIconType.emoji:
|
||||||
|
return ViewIconTypePB.Emoji;
|
||||||
|
case FlowyIconType.icon:
|
||||||
|
return ViewIconTypePB.Icon;
|
||||||
|
case FlowyIconType.custom:
|
||||||
|
return ViewIconTypePB.Url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FlowyIconType {
|
||||||
|
emoji,
|
||||||
|
icon,
|
||||||
|
custom;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmojiPickerResult {
|
||||||
|
factory EmojiPickerResult.none() =>
|
||||||
|
const EmojiPickerResult(FlowyIconType.icon, '');
|
||||||
|
|
||||||
|
factory EmojiPickerResult.emoji(String emoji) =>
|
||||||
|
EmojiPickerResult(FlowyIconType.emoji, emoji);
|
||||||
|
|
||||||
|
const EmojiPickerResult(
|
||||||
|
this.type,
|
||||||
|
this.emoji,
|
||||||
|
);
|
||||||
|
|
||||||
|
final FlowyIconType type;
|
||||||
|
final String emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlowyIconEmojiPicker extends StatefulWidget {
|
||||||
|
const FlowyIconEmojiPicker({
|
||||||
|
super.key,
|
||||||
|
this.onSelectedEmoji,
|
||||||
|
this.onSelectedIcon,
|
||||||
|
this.tabs = const [PickerTabType.emoji],
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function(EmojiPickerResult result)? onSelectedEmoji;
|
||||||
|
final void Function(IconGroup? group, Icon? icon, String? color)?
|
||||||
|
onSelectedIcon;
|
||||||
|
final List<PickerTabType> tabs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FlowyIconEmojiPicker> createState() => _FlowyIconEmojiPickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FlowyIconEmojiPickerState extends State<FlowyIconEmojiPicker>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late final controller = TabController(
|
||||||
|
length: widget.tabs.length,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
int currentIndex = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 46,
|
||||||
|
padding: const EdgeInsets.only(left: 4.0, right: 12.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: PickerTab(
|
||||||
|
controller: controller,
|
||||||
|
tabs: widget.tabs,
|
||||||
|
onTap: (index) => currentIndex = index,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_RemoveIconButton(
|
||||||
|
onTap: () {
|
||||||
|
final currentTab = widget.tabs[currentIndex];
|
||||||
|
if (currentTab == PickerTabType.emoji) {
|
||||||
|
widget.onSelectedEmoji?.call(
|
||||||
|
EmojiPickerResult.none(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
widget.onSelectedIcon?.call(null, null, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const FlowyDivider(),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
controller: controller,
|
||||||
|
children: widget.tabs.map((tab) {
|
||||||
|
switch (tab) {
|
||||||
|
case PickerTabType.emoji:
|
||||||
|
return _buildEmojiPicker();
|
||||||
|
case PickerTabType.icon:
|
||||||
|
return _buildIconPicker();
|
||||||
|
}
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEmojiPicker() {
|
||||||
|
return FlowyEmojiPicker(
|
||||||
|
emojiPerLine: _getEmojiPerLine(context),
|
||||||
|
onEmojiSelected: (_, emoji) => widget.onSelectedEmoji?.call(
|
||||||
|
EmojiPickerResult.emoji(emoji),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _getEmojiPerLine(BuildContext context) {
|
||||||
|
if (PlatformExtension.isDesktopOrWeb) {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
return width ~/ 40.0; // the size of the emoji
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIconPicker() {
|
||||||
|
return Expanded(
|
||||||
|
child: FlowyIconPicker(
|
||||||
|
onSelectedIcon: (iconGroup, icon, color) {
|
||||||
|
debugPrint('icon: ${icon.toJson()}, color: $color');
|
||||||
|
widget.onSelectedIcon?.call(iconGroup, icon, color);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RemoveIconButton extends StatelessWidget {
|
||||||
|
const _RemoveIconButton({required this.onTap});
|
||||||
|
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 32,
|
||||||
|
child: FlowyButton(
|
||||||
|
onTap: onTap,
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
text: FlowyText(
|
||||||
|
fontSize: 14.0,
|
||||||
|
figmaLineHeight: 16.0,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
LocaleKeys.button_remove.tr(),
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'icon.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class IconGroup {
|
||||||
|
factory IconGroup.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$IconGroupFromJson(json);
|
||||||
|
|
||||||
|
factory IconGroup.fromMapEntry(MapEntry<String, dynamic> entry) =>
|
||||||
|
IconGroup.fromJson({
|
||||||
|
'name': entry.key,
|
||||||
|
'icons': entry.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
IconGroup({
|
||||||
|
required this.name,
|
||||||
|
required this.icons,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final List<Icon> icons;
|
||||||
|
|
||||||
|
String get displayName => name.replaceAll('_', ' ');
|
||||||
|
|
||||||
|
IconGroup filter(String keyword) {
|
||||||
|
final filteredIcons = icons
|
||||||
|
.where(
|
||||||
|
(icon) => icon.keywords.any((k) => k.contains(keyword.toLowerCase())),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
return IconGroup(name: name, icons: filteredIcons);
|
||||||
|
}
|
||||||
|
|
||||||
|
String? getSvgContent(String iconName) {
|
||||||
|
final icon = icons.firstWhere(
|
||||||
|
(icon) => icon.name == iconName,
|
||||||
|
);
|
||||||
|
return icon.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$IconGroupToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class Icon {
|
||||||
|
factory Icon.fromJson(Map<String, dynamic> json) => _$IconFromJson(json);
|
||||||
|
|
||||||
|
Icon({
|
||||||
|
required this.name,
|
||||||
|
required this.keywords,
|
||||||
|
required this.content,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final List<String> keywords;
|
||||||
|
final String content;
|
||||||
|
|
||||||
|
String get displayName => name.replaceAll('-', ' ');
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$IconToJson(this);
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class IconColorPicker extends StatelessWidget {
|
||||||
|
const IconColorPicker({
|
||||||
|
super.key,
|
||||||
|
required this.onSelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function(String color) onSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GridView.count(
|
||||||
|
shrinkWrap: true,
|
||||||
|
crossAxisCount: 6,
|
||||||
|
mainAxisSpacing: 4.0,
|
||||||
|
children: builtInSpaceColors.map((color) {
|
||||||
|
return FlowyHover(
|
||||||
|
style: HoverStyle(borderRadius: BorderRadius.circular(8.0)),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => onSelected(color),
|
||||||
|
child: Container(
|
||||||
|
width: 34,
|
||||||
|
height: 34,
|
||||||
|
padding: const EdgeInsets.all(5.0),
|
||||||
|
child: Container(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
color: Color(int.parse(color)),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
side: const BorderSide(color: Color(0x2D333333)),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,271 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/icon.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/icon_search_bar.dart';
|
||||||
|
import 'package:appflowy/util/debounce.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart' hide Icon;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'icon_color_picker.dart';
|
||||||
|
|
||||||
|
// cache the icon groups to avoid loading them multiple times
|
||||||
|
List<IconGroup>? kIconGroups;
|
||||||
|
|
||||||
|
extension IconGroupFilter on List<IconGroup> {
|
||||||
|
String? findSvgContent(String key) {
|
||||||
|
final values = key.split('/');
|
||||||
|
if (values.length != 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final groupName = values[0];
|
||||||
|
final iconName = values[1];
|
||||||
|
final svgString = kIconGroups
|
||||||
|
?.firstWhereOrNull(
|
||||||
|
(group) => group.name == groupName,
|
||||||
|
)
|
||||||
|
?.icons
|
||||||
|
.firstWhereOrNull(
|
||||||
|
(icon) => icon.name == iconName,
|
||||||
|
)
|
||||||
|
?.content;
|
||||||
|
return svgString;
|
||||||
|
}
|
||||||
|
|
||||||
|
(IconGroup, Icon) randomIcon() {
|
||||||
|
final random = Random();
|
||||||
|
final group = this[random.nextInt(length)];
|
||||||
|
final icon = group.icons[random.nextInt(group.icons.length)];
|
||||||
|
return (group, icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<IconGroup>> loadIconGroups() async {
|
||||||
|
if (kIconGroups != null) {
|
||||||
|
return kIconGroups!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
final jsonString = await rootBundle.loadString('assets/icons/icons.json');
|
||||||
|
try {
|
||||||
|
final json = jsonDecode(jsonString) as Map<String, dynamic>;
|
||||||
|
final iconGroups = json.entries.map(IconGroup.fromMapEntry).toList();
|
||||||
|
kIconGroups = iconGroups;
|
||||||
|
return iconGroups;
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Failed to decode icons.json', e);
|
||||||
|
return [];
|
||||||
|
} finally {
|
||||||
|
stopwatch.stop();
|
||||||
|
Log.info('Loaded icon groups in ${stopwatch.elapsedMilliseconds}ms');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlowyIconPicker extends StatefulWidget {
|
||||||
|
const FlowyIconPicker({
|
||||||
|
super.key,
|
||||||
|
required this.onSelectedIcon,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function(IconGroup group, Icon icon, String color) onSelectedIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FlowyIconPicker> createState() => _FlowyIconPickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FlowyIconPickerState extends State<FlowyIconPicker> {
|
||||||
|
late final Future<List<IconGroup>> iconGroups;
|
||||||
|
final ValueNotifier<String> keyword = ValueNotifier('');
|
||||||
|
final debounce = Debounce(duration: const Duration(milliseconds: 150));
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
iconGroups = loadIconGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
keyword.dispose();
|
||||||
|
debounce.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: IconSearchBar(
|
||||||
|
onRandomTap: () {
|
||||||
|
final value = kIconGroups?.randomIcon();
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final color = generateRandomSpaceColor();
|
||||||
|
widget.onSelectedIcon(value.$1, value.$2, color);
|
||||||
|
},
|
||||||
|
onKeywordChanged: (keyword) => {
|
||||||
|
debounce.call(() {
|
||||||
|
this.keyword.value = keyword;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: kIconGroups != null
|
||||||
|
? _buildIcons(kIconGroups!)
|
||||||
|
: FutureBuilder(
|
||||||
|
future: iconGroups,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState != ConnectionState.done) {
|
||||||
|
return const Center(
|
||||||
|
child: SizedBox.square(
|
||||||
|
dimension: 24.0,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final iconGroups = snapshot.data as List<IconGroup>;
|
||||||
|
return _buildIcons(iconGroups);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIcons(List<IconGroup> iconGroups) {
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: keyword,
|
||||||
|
builder: (_, keyword, __) {
|
||||||
|
if (keyword.isNotEmpty) {
|
||||||
|
final filteredIconGroups = iconGroups
|
||||||
|
.map((iconGroup) => iconGroup.filter(keyword))
|
||||||
|
.where((iconGroup) => iconGroup.icons.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
return IconPicker(
|
||||||
|
iconGroups: filteredIconGroups,
|
||||||
|
onSelectedIcon: widget.onSelectedIcon,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return IconPicker(
|
||||||
|
iconGroups: iconGroups,
|
||||||
|
onSelectedIcon: widget.onSelectedIcon,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IconPicker extends StatefulWidget {
|
||||||
|
const IconPicker({
|
||||||
|
super.key,
|
||||||
|
required this.onSelectedIcon,
|
||||||
|
required this.iconGroups,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<IconGroup> iconGroups;
|
||||||
|
final void Function(IconGroup group, Icon icon, String color) onSelectedIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<IconPicker> createState() => _IconPickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IconPickerState extends State<IconPicker> {
|
||||||
|
final mutex = PopoverMutex();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: widget.iconGroups.length,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final iconGroup = widget.iconGroups[index];
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
FlowyText(
|
||||||
|
iconGroup.displayName,
|
||||||
|
fontSize: 12,
|
||||||
|
figmaLineHeight: 18.0,
|
||||||
|
color: const Color(0x80171717),
|
||||||
|
),
|
||||||
|
const VSpace(4.0),
|
||||||
|
Wrap(
|
||||||
|
children: iconGroup.icons.map(
|
||||||
|
(icon) {
|
||||||
|
return _Icon(
|
||||||
|
icon: icon,
|
||||||
|
mutex: mutex,
|
||||||
|
onSelectedColor: (context, color) {
|
||||||
|
widget.onSelectedIcon(iconGroup, icon, color);
|
||||||
|
PopoverContainer.of(context).close();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
const VSpace(12.0),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Icon extends StatelessWidget {
|
||||||
|
const _Icon({
|
||||||
|
required this.icon,
|
||||||
|
required this.mutex,
|
||||||
|
required this.onSelectedColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Icon icon;
|
||||||
|
final PopoverMutex mutex;
|
||||||
|
final void Function(BuildContext context, String color) onSelectedColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AppFlowyPopover(
|
||||||
|
direction: PopoverDirection.bottomWithCenterAligned,
|
||||||
|
offset: const Offset(0, 6),
|
||||||
|
mutex: mutex,
|
||||||
|
child: FlowyTooltip(
|
||||||
|
message: icon.displayName,
|
||||||
|
preferBelow: false,
|
||||||
|
child: FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
margin: const EdgeInsets.all(8.0),
|
||||||
|
text: Center(
|
||||||
|
child: FlowySvg.string(
|
||||||
|
icon.content,
|
||||||
|
size: const Size.square(20),
|
||||||
|
color: const Color(0xFF171717),
|
||||||
|
opacity: 0.7,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
popupBuilder: (context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(6.0),
|
||||||
|
child: IconColorPicker(
|
||||||
|
onSelected: (color) => onSelectedColor(context, color),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||||
|
|
||||||
|
typedef IconKeywordChangedCallback = void Function(String keyword);
|
||||||
|
typedef EmojiSkinToneChanged = void Function(EmojiSkinTone skinTone);
|
||||||
|
|
||||||
|
class IconSearchBar extends StatefulWidget {
|
||||||
|
const IconSearchBar({
|
||||||
|
super.key,
|
||||||
|
required this.onRandomTap,
|
||||||
|
required this.onKeywordChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
final VoidCallback onRandomTap;
|
||||||
|
final IconKeywordChangedCallback onKeywordChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<IconSearchBar> createState() => _IconSearchBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IconSearchBarState extends State<IconSearchBar> {
|
||||||
|
final TextEditingController controller = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 12.0,
|
||||||
|
horizontal: PlatformExtension.isDesktopOrWeb ? 0.0 : 8.0,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _SearchTextField(
|
||||||
|
onKeywordChanged: widget.onKeywordChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const HSpace(8.0),
|
||||||
|
_RandomIconButton(
|
||||||
|
onRandomTap: widget.onRandomTap,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RandomIconButton extends StatelessWidget {
|
||||||
|
const _RandomIconButton({
|
||||||
|
required this.onRandomTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final VoidCallback onRandomTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
side: const BorderSide(color: Color(0x1E171717)),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: FlowyTooltip(
|
||||||
|
message: LocaleKeys.emoji_random.tr(),
|
||||||
|
child: FlowyButton(
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
text: const FlowySvg(
|
||||||
|
FlowySvgs.icon_shuffle_s,
|
||||||
|
),
|
||||||
|
onTap: onRandomTap,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchTextField extends StatefulWidget {
|
||||||
|
const _SearchTextField({
|
||||||
|
required this.onKeywordChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
final IconKeywordChangedCallback onKeywordChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_SearchTextField> createState() => _SearchTextFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchTextFieldState extends State<_SearchTextField> {
|
||||||
|
final TextEditingController controller = TextEditingController();
|
||||||
|
final FocusNode focusNode = FocusNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.dispose();
|
||||||
|
focusNode.dispose();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 36.0,
|
||||||
|
child: FlowyTextField(
|
||||||
|
focusNode: focusNode,
|
||||||
|
hintText: LocaleKeys.search_label.tr(),
|
||||||
|
hintStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
enableBorderColor: const Color(0x1E171717),
|
||||||
|
controller: controller,
|
||||||
|
onChanged: widget.onKeywordChanged,
|
||||||
|
prefixIcon: const Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: 14.0,
|
||||||
|
right: 8.0,
|
||||||
|
),
|
||||||
|
child: FlowySvg(
|
||||||
|
FlowySvgs.search_s,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
prefixIconConstraints: const BoxConstraints(
|
||||||
|
maxHeight: 20.0,
|
||||||
|
),
|
||||||
|
suffixIcon: Padding(
|
||||||
|
padding: const EdgeInsets.all(4.0),
|
||||||
|
child: FlowyButton(
|
||||||
|
text: const FlowySvg(
|
||||||
|
FlowySvgs.m_app_bar_close_s,
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
useIntrinsicWidth: true,
|
||||||
|
onTap: () {
|
||||||
|
if (controller.text.isNotEmpty) {
|
||||||
|
controller.clear();
|
||||||
|
widget.onKeywordChanged('');
|
||||||
|
} else {
|
||||||
|
focusNode.unfocus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
import 'package:appflowy/mobile/presentation/home/tab/_round_underline_tab_indicator.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum PickerTabType {
|
||||||
|
emoji,
|
||||||
|
icon;
|
||||||
|
|
||||||
|
String get tr {
|
||||||
|
switch (this) {
|
||||||
|
case PickerTabType.emoji:
|
||||||
|
return 'Emojis';
|
||||||
|
case PickerTabType.icon:
|
||||||
|
return 'Icons';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PickerTab extends StatelessWidget {
|
||||||
|
const PickerTab({
|
||||||
|
super.key,
|
||||||
|
this.onTap,
|
||||||
|
required this.controller,
|
||||||
|
required this.tabs,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<PickerTabType> tabs;
|
||||||
|
final TabController controller;
|
||||||
|
final ValueChanged<int>? onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final baseStyle = Theme.of(context).textTheme.bodyMedium;
|
||||||
|
final style = baseStyle?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14.0,
|
||||||
|
height: 16.0 / 14.0,
|
||||||
|
);
|
||||||
|
return TabBar(
|
||||||
|
controller: controller,
|
||||||
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
|
indicatorColor: Theme.of(context).colorScheme.primary,
|
||||||
|
isScrollable: true,
|
||||||
|
labelStyle: style,
|
||||||
|
labelColor: baseStyle?.color,
|
||||||
|
labelPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
unselectedLabelStyle: style?.copyWith(
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
|
indicator: RoundUnderlineTabIndicator(
|
||||||
|
width: 34.0,
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
width: 3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: onTap,
|
||||||
|
tabs: tabs
|
||||||
|
.map(
|
||||||
|
(tab) => Tab(
|
||||||
|
text: tab.tr,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||||
import 'package:appflowy/shared/feature_flags.dart';
|
import 'package:appflowy/shared/feature_flags.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/user/application/user_settings_service.dart';
|
import 'package:appflowy/user/application/user_settings_service.dart';
|
||||||
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
||||||
@ -41,6 +42,8 @@ class InitAppWidgetTask extends LaunchTask {
|
|||||||
|
|
||||||
await NotificationService.initialize();
|
await NotificationService.initialize();
|
||||||
|
|
||||||
|
await loadIconGroups();
|
||||||
|
|
||||||
final widget = context.getIt<EntryPoint>().create(context.config);
|
final widget = context.getIt<EntryPoint>().create(context.config);
|
||||||
final appearanceSetting =
|
final appearanceSetting =
|
||||||
await UserSettingsBackendService().getAppearanceSetting();
|
await UserSettingsBackendService().getAppearanceSetting();
|
||||||
|
@ -183,6 +183,21 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error('Failed to migrating cover: $e');
|
Log.error('Failed to migrating cover: $e');
|
||||||
}
|
}
|
||||||
|
} else if (icon == null) {
|
||||||
|
try {
|
||||||
|
final extra = space.extra;
|
||||||
|
final Map<String, dynamic> current = extra.isNotEmpty == true
|
||||||
|
? jsonDecode(extra)
|
||||||
|
: <String, dynamic>{};
|
||||||
|
current.remove(ViewExtKeys.spaceIconKey);
|
||||||
|
current.remove(ViewExtKeys.spaceIconColorKey);
|
||||||
|
await ViewBackendService.updateView(
|
||||||
|
viewId: space.id,
|
||||||
|
extra: jsonEncode(current),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Failed to migrating cover: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permission != null) {
|
if (permission != null) {
|
||||||
@ -691,8 +706,10 @@ class SpaceEvent with _$SpaceEvent {
|
|||||||
required bool createNewPageByDefault,
|
required bool createNewPageByDefault,
|
||||||
}) = _Create;
|
}) = _Create;
|
||||||
const factory SpaceEvent.rename(ViewPB space, String name) = _Rename;
|
const factory SpaceEvent.rename(ViewPB space, String name) = _Rename;
|
||||||
const factory SpaceEvent.changeIcon(String icon, String iconColor) =
|
const factory SpaceEvent.changeIcon(
|
||||||
_ChangeIcon;
|
String? icon,
|
||||||
|
String? iconColor,
|
||||||
|
) = _ChangeIcon;
|
||||||
const factory SpaceEvent.duplicate() = _Duplicate;
|
const factory SpaceEvent.duplicate() = _Duplicate;
|
||||||
const factory SpaceEvent.update({
|
const factory SpaceEvent.update({
|
||||||
String? name,
|
String? name,
|
||||||
|
@ -9,10 +9,12 @@ import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
|||||||
import 'package:appflowy/plugins/database/grid/presentation/mobile_grid_page.dart';
|
import 'package:appflowy/plugins/database/grid/presentation/mobile_grid_page.dart';
|
||||||
import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
|
import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
|
||||||
import 'package:appflowy/plugins/document/document.dart';
|
import 'package:appflowy/plugins/document/document.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
|
||||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class PluginArgumentKeys {
|
class PluginArgumentKeys {
|
||||||
@ -134,7 +136,7 @@ extension ViewExtension on ViewPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlowySvg? get spaceIconSvg {
|
FlowySvg? buildSpaceIconSvg(BuildContext context, {Size? size}) {
|
||||||
try {
|
try {
|
||||||
final ext = jsonDecode(extra);
|
final ext = jsonDecode(extra);
|
||||||
final icon = ext[ViewExtKeys.spaceIconKey];
|
final icon = ext[ViewExtKeys.spaceIconKey];
|
||||||
@ -142,10 +144,36 @@ extension ViewExtension on ViewPB {
|
|||||||
if (icon == null || color == null) {
|
if (icon == null || color == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// before version 0.6.7
|
||||||
|
if (icon.contains('space_icon')) {
|
||||||
return FlowySvg(
|
return FlowySvg(
|
||||||
FlowySvgData('assets/flowy_icons/16x/$icon.svg'),
|
FlowySvgData('assets/flowy_icons/16x/$icon.svg'),
|
||||||
color: Color(int.parse(color)),
|
color: Theme.of(context).colorScheme.surface,
|
||||||
blendMode: BlendMode.srcOut,
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final values = icon.split('/');
|
||||||
|
if (values.length != 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final groupName = values[0];
|
||||||
|
final iconName = values[1];
|
||||||
|
final svgString = kIconGroups
|
||||||
|
?.firstWhereOrNull(
|
||||||
|
(group) => group.name == groupName,
|
||||||
|
)
|
||||||
|
?.icons
|
||||||
|
.firstWhereOrNull(
|
||||||
|
(icon) => icon.name == iconName,
|
||||||
|
)
|
||||||
|
?.content;
|
||||||
|
if (svgString == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return FlowySvg.string(
|
||||||
|
svgString,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
size: size,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -5,7 +5,6 @@ import 'package:appflowy/workspace/application/view/view_ext.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
|||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
|||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ class CreateSpacePopup extends StatefulWidget {
|
|||||||
|
|
||||||
class _CreateSpacePopupState extends State<CreateSpacePopup> {
|
class _CreateSpacePopupState extends State<CreateSpacePopup> {
|
||||||
String spaceName = LocaleKeys.space_defaultSpaceName.tr();
|
String spaceName = LocaleKeys.space_defaultSpaceName.tr();
|
||||||
String spaceIcon = builtInSpaceIcons.first;
|
String? spaceIcon = builtInSpaceIcons.first;
|
||||||
String spaceIconColor = builtInSpaceColors.first;
|
String? spaceIconColor = builtInSpaceColors.first;
|
||||||
SpacePermission spacePermission = SpacePermission.publicToAll;
|
SpacePermission spacePermission = SpacePermission.publicToAll;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -80,8 +80,9 @@ class _CreateSpacePopupState extends State<CreateSpacePopup> {
|
|||||||
context.read<SpaceBloc>().add(
|
context.read<SpaceBloc>().add(
|
||||||
SpaceEvent.create(
|
SpaceEvent.create(
|
||||||
name: spaceName,
|
name: spaceName,
|
||||||
icon: spaceIcon,
|
// fixme: space issue
|
||||||
iconColor: spaceIconColor,
|
icon: spaceIcon!,
|
||||||
|
iconColor: spaceIconColor!,
|
||||||
permission: spacePermission,
|
permission: spacePermission,
|
||||||
createNewPageByDefault: true,
|
createNewPageByDefault: true,
|
||||||
),
|
),
|
||||||
|
@ -77,7 +77,7 @@ class _SpaceNameTextField extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
final void Function(String name) onNameChanged;
|
final void Function(String name) onNameChanged;
|
||||||
final void Function(String icon, String color) onIconChanged;
|
final void Function(String? icon, String? color) onIconChanged;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/util/theme_extension.dart';
|
import 'package:appflowy/util/theme_extension.dart';
|
||||||
@ -21,7 +17,9 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class SpacePermissionSwitch extends StatefulWidget {
|
class SpacePermissionSwitch extends StatefulWidget {
|
||||||
@ -280,9 +278,9 @@ class ConfirmPopupColor {
|
|||||||
|
|
||||||
static Color descriptionColor(BuildContext context) {
|
static Color descriptionColor(BuildContext context) {
|
||||||
if (Theme.of(context).isLightMode) {
|
if (Theme.of(context).isLightMode) {
|
||||||
return const Color(0xFF171717).withOpacity(0.8);
|
return const Color(0xFF171717).withOpacity(0.7);
|
||||||
}
|
}
|
||||||
return const Color(0xFFffffff).withOpacity(0.72);
|
return const Color(0xFFffffff).withOpacity(0.7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,15 +372,21 @@ class _ConfirmPopupState extends State<ConfirmPopup> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: FlowyText(
|
child: FlowyText(
|
||||||
widget.title,
|
widget.title,
|
||||||
fontSize: 14.0,
|
fontSize: 16.0,
|
||||||
|
figmaLineHeight: 22.0,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
color: ConfirmPopupColor.titleColor(context),
|
color: ConfirmPopupColor.titleColor(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const HSpace(6.0),
|
const HSpace(6.0),
|
||||||
FlowyButton(
|
FlowyButton(
|
||||||
|
margin: const EdgeInsets.all(3),
|
||||||
useIntrinsicWidth: true,
|
useIntrinsicWidth: true,
|
||||||
text: const FlowySvg(FlowySvgs.upgrade_close_s),
|
text: const FlowySvg(
|
||||||
|
FlowySvgs.upgrade_close_s,
|
||||||
|
size: Size.square(18.0),
|
||||||
|
),
|
||||||
onTap: () => Navigator.of(context).pop(),
|
onTap: () => Navigator.of(context).pop(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -392,10 +396,10 @@ class _ConfirmPopupState extends State<ConfirmPopup> {
|
|||||||
Widget _buildDescription() {
|
Widget _buildDescription() {
|
||||||
return FlowyText.regular(
|
return FlowyText.regular(
|
||||||
widget.description,
|
widget.description,
|
||||||
fontSize: 12.0,
|
fontSize: 16.0,
|
||||||
color: ConfirmPopupColor.descriptionColor(context),
|
color: ConfirmPopupColor.descriptionColor(context),
|
||||||
maxLines: 3,
|
maxLines: 5,
|
||||||
lineHeight: 1.4,
|
figmaLineHeight: 22.0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,16 +496,22 @@ class CurrentSpace extends StatelessWidget {
|
|||||||
final child = Row(
|
final child = Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
if (space.spaceIcon != null) ...[
|
||||||
SpaceIcon(
|
SpaceIcon(
|
||||||
dimension: 20,
|
dimension: 22,
|
||||||
space: space,
|
space: space,
|
||||||
cornerRadius: 6.0,
|
svgSize: 13,
|
||||||
|
cornerRadius: 8.0,
|
||||||
),
|
),
|
||||||
const HSpace(10),
|
const HSpace(10),
|
||||||
|
] else ...[
|
||||||
|
const HSpace(2),
|
||||||
|
],
|
||||||
Flexible(
|
Flexible(
|
||||||
child: FlowyText.medium(
|
child: FlowyText.medium(
|
||||||
space.name,
|
space.name,
|
||||||
fontSize: 14.0,
|
fontSize: 14.0,
|
||||||
|
figmaLineHeight: 18.0,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/icon.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/manage_space_popup.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/manage_space_popup.dart';
|
||||||
@ -13,8 +14,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
import 'package:flutter/material.dart' hide Icon;
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class SidebarSpaceHeader extends StatefulWidget {
|
class SidebarSpaceHeader extends StatefulWidget {
|
||||||
@ -171,8 +171,19 @@ class _SidebarSpaceHeaderState extends State<SidebarSpaceHeader> {
|
|||||||
await _showRenameDialog();
|
await _showRenameDialog();
|
||||||
break;
|
break;
|
||||||
case SpaceMoreActionType.changeIcon:
|
case SpaceMoreActionType.changeIcon:
|
||||||
final (String icon, String iconColor) = data;
|
final (IconGroup? group, Icon? icon, String? iconColor) = data;
|
||||||
context.read<SpaceBloc>().add(SpaceEvent.changeIcon(icon, iconColor));
|
|
||||||
|
final groupName = group?.name;
|
||||||
|
final iconName = icon?.name;
|
||||||
|
final name = groupName != null && iconName != null
|
||||||
|
? '$groupName/$iconName'
|
||||||
|
: null;
|
||||||
|
context.read<SpaceBloc>().add(
|
||||||
|
SpaceEvent.changeIcon(
|
||||||
|
name,
|
||||||
|
iconColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case SpaceMoreActionType.manage:
|
case SpaceMoreActionType.manage:
|
||||||
_showManageSpaceDialog(context);
|
_showManageSpaceDialog(context);
|
||||||
|
@ -9,7 +9,6 @@ import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -8,19 +10,71 @@ class SpaceIcon extends StatelessWidget {
|
|||||||
required this.dimension,
|
required this.dimension,
|
||||||
this.cornerRadius = 0,
|
this.cornerRadius = 0,
|
||||||
required this.space,
|
required this.space,
|
||||||
|
this.svgSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
final double dimension;
|
final double dimension;
|
||||||
final double cornerRadius;
|
final double cornerRadius;
|
||||||
final ViewPB space;
|
final ViewPB space;
|
||||||
|
final double? svgSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox.square(
|
final spaceIconColor = space.spaceIconColor;
|
||||||
dimension: dimension,
|
final color = spaceIconColor != null
|
||||||
child: ClipRRect(
|
? Color(int.parse(spaceIconColor))
|
||||||
|
: Colors.transparent;
|
||||||
|
final svg = space.buildSpaceIconSvg(
|
||||||
|
context,
|
||||||
|
size: svgSize != null ? Size.square(svgSize!) : null,
|
||||||
|
);
|
||||||
|
if (svg == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(cornerRadius),
|
borderRadius: BorderRadius.circular(cornerRadius),
|
||||||
child: space.spaceIconSvg,
|
child: Container(
|
||||||
|
width: dimension,
|
||||||
|
height: dimension,
|
||||||
|
color: color,
|
||||||
|
child: Center(
|
||||||
|
child: svgSize == null
|
||||||
|
? svg
|
||||||
|
: SizedBox.square(dimension: svgSize!, child: svg),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultSpaceIcon extends StatelessWidget {
|
||||||
|
const DefaultSpaceIcon({
|
||||||
|
super.key,
|
||||||
|
required this.dimension,
|
||||||
|
required this.iconDimension,
|
||||||
|
this.cornerRadius = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
final double dimension;
|
||||||
|
final double cornerRadius;
|
||||||
|
final double iconDimension;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final svg = builtInSpaceIcons.first;
|
||||||
|
final color = Color(int.parse(builtInSpaceColors.first));
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(cornerRadius),
|
||||||
|
child: Container(
|
||||||
|
width: dimension,
|
||||||
|
height: dimension,
|
||||||
|
color: color,
|
||||||
|
child: FlowySvg(
|
||||||
|
FlowySvgData('assets/flowy_icons/16x/$svg.svg'),
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
size: Size.square(iconDimension),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/tab.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart' hide Icon;
|
||||||
|
|
||||||
final builtInSpaceColors = [
|
final builtInSpaceColors = [
|
||||||
'0xFFA34AFD',
|
'0xFFA34AFD',
|
||||||
@ -20,6 +26,11 @@ final builtInSpaceColors = [
|
|||||||
'0xFFFF8933',
|
'0xFFFF8933',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
String generateRandomSpaceColor() {
|
||||||
|
final random = Random();
|
||||||
|
return builtInSpaceColors[random.nextInt(builtInSpaceColors.length)];
|
||||||
|
}
|
||||||
|
|
||||||
final builtInSpaceIcons =
|
final builtInSpaceIcons =
|
||||||
List.generate(15, (index) => 'space_icon_${index + 1}');
|
List.generate(15, (index) => 'space_icon_${index + 1}');
|
||||||
|
|
||||||
@ -34,7 +45,7 @@ class SpaceIconPopup extends StatefulWidget {
|
|||||||
|
|
||||||
final String? icon;
|
final String? icon;
|
||||||
final String? iconColor;
|
final String? iconColor;
|
||||||
final void Function(String icon, String color) onIconChanged;
|
final void Function(String? icon, String? color) onIconChanged;
|
||||||
final double cornerRadius;
|
final double cornerRadius;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -42,10 +53,12 @@ class SpaceIconPopup extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SpaceIconPopupState extends State<SpaceIconPopup> {
|
class _SpaceIconPopupState extends State<SpaceIconPopup> {
|
||||||
late ValueNotifier<String> selectedColor =
|
late ValueNotifier<String?> selectedIcon = ValueNotifier<String?>(
|
||||||
ValueNotifier<String>(widget.iconColor ?? builtInSpaceColors.first);
|
widget.icon,
|
||||||
late ValueNotifier<String> selectedIcon =
|
);
|
||||||
ValueNotifier<String>(widget.icon ?? builtInSpaceIcons.first);
|
late ValueNotifier<String> selectedColor = ValueNotifier<String>(
|
||||||
|
widget.iconColor ?? builtInSpaceColors.first,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -58,19 +71,30 @@ class _SpaceIconPopupState extends State<SpaceIconPopup> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppFlowyPopover(
|
return AppFlowyPopover(
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
constraints: const BoxConstraints(maxWidth: 220),
|
constraints: BoxConstraints.loose(const Size(380, 432)),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 12.0),
|
margin: const EdgeInsets.all(0),
|
||||||
direction: PopoverDirection.bottomWithCenterAligned,
|
direction: PopoverDirection.bottomWithCenterAligned,
|
||||||
child: _buildPreview(),
|
child: _buildPreview(),
|
||||||
popupBuilder: (_) => SpaceIconPicker(
|
popupBuilder: (context) {
|
||||||
icon: selectedIcon.value,
|
return FlowyIconEmojiPicker(
|
||||||
iconColor: selectedColor.value,
|
tabs: const [PickerTabType.icon],
|
||||||
onIconChanged: (icon, iconColor) {
|
onSelectedIcon: (group, icon, color) {
|
||||||
selectedIcon.value = icon;
|
if (group == null || icon == null) {
|
||||||
selectedColor.value = iconColor;
|
selectedIcon.value = null;
|
||||||
widget.onIconChanged(icon, iconColor);
|
} else {
|
||||||
|
selectedIcon.value = '${group.name}/${icon.name}';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color != null) {
|
||||||
|
selectedColor.value = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.onIconChanged(selectedIcon.value, selectedColor.value);
|
||||||
|
|
||||||
|
PopoverContainer.of(context).close();
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,15 +111,36 @@ class _SpaceIconPopupState extends State<SpaceIconPopup> {
|
|||||||
builder: (_, color, __) {
|
builder: (_, color, __) {
|
||||||
return ValueListenableBuilder(
|
return ValueListenableBuilder(
|
||||||
valueListenable: selectedIcon,
|
valueListenable: selectedIcon,
|
||||||
builder: (_, icon, __) {
|
builder: (_, value, __) {
|
||||||
final child = ClipRRect(
|
Widget child;
|
||||||
borderRadius: BorderRadius.circular(widget.cornerRadius),
|
if (value == null) {
|
||||||
child: FlowySvg(
|
child = const DefaultSpaceIcon(
|
||||||
FlowySvgData('assets/flowy_icons/16x/$icon.svg'),
|
cornerRadius: 16.0,
|
||||||
|
dimension: 32,
|
||||||
|
iconDimension: 32,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final content = kIconGroups?.findSvgContent(value);
|
||||||
|
if (content == null) {
|
||||||
|
child = const SizedBox.shrink();
|
||||||
|
} else {
|
||||||
|
child = ClipRRect(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(widget.cornerRadius),
|
||||||
|
child: Container(
|
||||||
color: Color(int.parse(color)),
|
color: Color(int.parse(color)),
|
||||||
blendMode: BlendMode.srcOut,
|
child: Align(
|
||||||
|
child: FlowySvg.string(
|
||||||
|
content,
|
||||||
|
size: const Size.square(24),
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (onHover) {
|
if (onHover) {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
|
import 'package:appflowy/shared/icon_emoji_picker/tab.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_action_type.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_action_type.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
|
|
||||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -108,20 +107,19 @@ class SpaceMoreActionTypeWrapper extends CustomActionCell {
|
|||||||
PopoverController controller,
|
PopoverController controller,
|
||||||
) {
|
) {
|
||||||
final child = _buildActionButton(context, null);
|
final child = _buildActionButton(context, null);
|
||||||
final spaceBloc = context.read<SpaceBloc>();
|
|
||||||
final color = spaceBloc.state.currentSpace?.spaceIconColor;
|
|
||||||
|
|
||||||
return AppFlowyPopover(
|
return AppFlowyPopover(
|
||||||
constraints: BoxConstraints.loose(const Size(216, 256)),
|
constraints: BoxConstraints.loose(const Size(380, 432)),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 12.0),
|
margin: const EdgeInsets.all(0),
|
||||||
clickHandler: PopoverClickHandler.gestureDetector,
|
clickHandler: PopoverClickHandler.gestureDetector,
|
||||||
popupBuilder: (_) => SpaceIconPicker(
|
offset: const Offset(0, -40),
|
||||||
iconColor: color,
|
popupBuilder: (context) {
|
||||||
skipFirstNotification: true,
|
return FlowyIconEmojiPicker(
|
||||||
onIconChanged: (icon, color) {
|
tabs: const [PickerTabType.icon],
|
||||||
onTap(controller, (icon, color));
|
onSelectedIcon: (group, icon, color) {
|
||||||
|
onTap(controller, (group, icon, color));
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -172,6 +170,8 @@ class SpaceMoreActionTypeWrapper extends CustomActionCell {
|
|||||||
rightIconBuilder: (_) => inner.rightIcon,
|
rightIconBuilder: (_) => inner.rightIcon,
|
||||||
textBuilder: (onHover) => FlowyText.regular(
|
textBuilder: (onHover) => FlowyText.regular(
|
||||||
inner.name,
|
inner.name,
|
||||||
|
fontSize: 14.0,
|
||||||
|
figmaLineHeight: 18.0,
|
||||||
color: inner == SpaceMoreActionType.delete && onHover
|
color: inner == SpaceMoreActionType.delete && onHover
|
||||||
? Theme.of(context).colorScheme.error
|
? Theme.of(context).colorScheme.error
|
||||||
: null,
|
: null,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/util/color_generator/color_generator.dart';
|
import 'package:appflowy/util/color_generator/color_generator.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
@ -18,6 +18,7 @@ class WorkspaceIcon extends StatefulWidget {
|
|||||||
this.borderRadius = 4,
|
this.borderRadius = 4,
|
||||||
this.emojiSize,
|
this.emojiSize,
|
||||||
this.alignment,
|
this.alignment,
|
||||||
|
required this.figmaLineHeight,
|
||||||
});
|
});
|
||||||
|
|
||||||
final UserWorkspacePB workspace;
|
final UserWorkspacePB workspace;
|
||||||
@ -28,6 +29,7 @@ class WorkspaceIcon extends StatefulWidget {
|
|||||||
final void Function(EmojiPickerResult) onSelected;
|
final void Function(EmojiPickerResult) onSelected;
|
||||||
final double borderRadius;
|
final double borderRadius;
|
||||||
final Alignment? alignment;
|
final Alignment? alignment;
|
||||||
|
final double figmaLineHeight;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<WorkspaceIcon> createState() => _WorkspaceIconState();
|
State<WorkspaceIcon> createState() => _WorkspaceIconState();
|
||||||
@ -45,7 +47,8 @@ class _WorkspaceIconState extends State<WorkspaceIcon> {
|
|||||||
child: FlowyText.emoji(
|
child: FlowyText.emoji(
|
||||||
widget.workspace.icon,
|
widget.workspace.icon,
|
||||||
fontSize: widget.emojiSize ?? widget.iconSize,
|
fontSize: widget.emojiSize ?? widget.iconSize,
|
||||||
figmaLineHeight: 21.0,
|
figmaLineHeight: widget.figmaLineHeight,
|
||||||
|
optimizeEmojiAlign: true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(
|
: Container(
|
||||||
@ -76,8 +79,9 @@ class _WorkspaceIconState extends State<WorkspaceIcon> {
|
|||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
constraints: BoxConstraints.loose(const Size(364, 356)),
|
constraints: BoxConstraints.loose(const Size(364, 356)),
|
||||||
clickHandler: PopoverClickHandler.gestureDetector,
|
clickHandler: PopoverClickHandler.gestureDetector,
|
||||||
popupBuilder: (_) => FlowyIconPicker(
|
margin: const EdgeInsets.all(0),
|
||||||
onSelected: (result) {
|
popupBuilder: (_) => FlowyIconEmojiPicker(
|
||||||
|
onSelectedEmoji: (result) {
|
||||||
widget.onSelected(result);
|
widget.onSelected(result);
|
||||||
controller.close();
|
controller.close();
|
||||||
},
|
},
|
||||||
|
@ -14,7 +14,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
|||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -172,6 +171,7 @@ class _WorkspaceMenuItemState extends State<WorkspaceMenuItem> {
|
|||||||
workspace: widget.workspace,
|
workspace: widget.workspace,
|
||||||
iconSize: 22,
|
iconSize: 22,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
figmaLineHeight: 32.0,
|
||||||
enableEdit: true,
|
enableEdit: true,
|
||||||
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
||||||
UserWorkspaceEvent.updateWorkspaceIcon(
|
UserWorkspaceEvent.updateWorkspaceIcon(
|
||||||
|
@ -242,6 +242,7 @@ class _SidebarSwitchWorkspaceButtonState
|
|||||||
emojiSize: 18,
|
emojiSize: 18,
|
||||||
enableEdit: false,
|
enableEdit: false,
|
||||||
borderRadius: 8.0,
|
borderRadius: 8.0,
|
||||||
|
figmaLineHeight: 21.0,
|
||||||
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
||||||
UserWorkspaceEvent.updateWorkspaceIcon(
|
UserWorkspaceEvent.updateWorkspaceIcon(
|
||||||
widget.currentWorkspace.workspaceId,
|
widget.currentWorkspace.workspaceId,
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
@ -28,7 +28,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -583,6 +582,7 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
controller: controller,
|
controller: controller,
|
||||||
direction: PopoverDirection.rightWithCenterAligned,
|
direction: PopoverDirection.rightWithCenterAligned,
|
||||||
constraints: BoxConstraints.loose(const Size(364, 356)),
|
constraints: BoxConstraints.loose(const Size(364, 356)),
|
||||||
|
margin: const EdgeInsets.all(0),
|
||||||
onClose: () => setState(() => isIconPickerOpened = false),
|
onClose: () => setState(() => isIconPickerOpened = false),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
// prevent the tap event from being passed to the parent widget
|
// prevent the tap event from being passed to the parent widget
|
||||||
@ -594,8 +594,8 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
),
|
),
|
||||||
popupBuilder: (context) {
|
popupBuilder: (context) {
|
||||||
isIconPickerOpened = true;
|
isIconPickerOpened = true;
|
||||||
return FlowyIconPicker(
|
return FlowyIconEmojiPicker(
|
||||||
onSelected: (result) {
|
onSelectedEmoji: (result) {
|
||||||
ViewBackendService.updateViewIcon(
|
ViewBackendService.updateViewIcon(
|
||||||
viewId: widget.view.id,
|
viewId: widget.view.id,
|
||||||
viewIcon: result.emoji,
|
viewIcon: result.emoji,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/move_to/move_page_menu.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/move_to/move_page_menu.dart';
|
||||||
@ -161,10 +161,10 @@ class ViewMoreActionTypeWrapper extends CustomActionCell {
|
|||||||
|
|
||||||
return AppFlowyPopover(
|
return AppFlowyPopover(
|
||||||
constraints: BoxConstraints.loose(const Size(364, 356)),
|
constraints: BoxConstraints.loose(const Size(364, 356)),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 12.0),
|
margin: const EdgeInsets.all(0),
|
||||||
clickHandler: PopoverClickHandler.gestureDetector,
|
clickHandler: PopoverClickHandler.gestureDetector,
|
||||||
popupBuilder: (_) => FlowyIconPicker(
|
popupBuilder: (_) => FlowyIconEmojiPicker(
|
||||||
onSelected: (result) => onTap(controller, result),
|
onSelectedEmoji: (result) => onTap(controller, result),
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
@ -256,6 +256,8 @@ class ViewMoreActionTypeWrapper extends CustomActionCell {
|
|||||||
iconPadding: 10.0,
|
iconPadding: 10.0,
|
||||||
textBuilder: (onHover) => FlowyText.regular(
|
textBuilder: (onHover) => FlowyText.regular(
|
||||||
inner.name,
|
inner.name,
|
||||||
|
fontSize: 14.0,
|
||||||
|
figmaLineHeight: 18.0,
|
||||||
color: inner == ViewMoreActionType.delete && onHover
|
color: inner == ViewMoreActionType.delete && onHover
|
||||||
? Theme.of(context).colorScheme.error
|
? Theme.of(context).colorScheme.error
|
||||||
: null,
|
: null,
|
||||||
|
@ -11,7 +11,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class NotificationButton extends StatefulWidget {
|
class NotificationButton extends StatefulWidget {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:appflowy/env/cloud_env.dart';
|
import 'package:appflowy/env/cloud_env.dart';
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||||
import 'package:appflowy/user/application/prelude.dart';
|
import 'package:appflowy/user/application/prelude.dart';
|
||||||
@ -18,7 +18,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -466,9 +465,9 @@ class _UserProfileSettingState extends State<UserProfileSetting> {
|
|||||||
Container(
|
Container(
|
||||||
height: 380,
|
height: 380,
|
||||||
width: 360,
|
width: 360,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12),
|
margin: const EdgeInsets.all(0),
|
||||||
child: FlowyIconPicker(
|
child: FlowyIconEmojiPicker(
|
||||||
onSelected: (r) {
|
onSelectedEmoji: (r) {
|
||||||
context
|
context
|
||||||
.read<SettingsUserViewBloc>()
|
.read<SettingsUserViewBloc>()
|
||||||
.add(SettingsUserEvent.updateUserIcon(iconUrl: r.emoji));
|
.add(SettingsUserEvent.updateUserIcon(iconUrl: r.emoji));
|
||||||
|
@ -11,7 +11,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
import '../../../../generated/locale_keys.g.dart';
|
import '../../../../generated/locale_keys.g.dart';
|
||||||
|
@ -23,7 +23,6 @@ import 'package:flowy_infra/theme_extension.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class SettingsShortcutsView extends StatefulWidget {
|
class SettingsShortcutsView extends StatefulWidget {
|
||||||
|
@ -356,6 +356,7 @@ class _WorkspaceIconSetting extends StatelessWidget {
|
|||||||
workspace: workspace!,
|
workspace: workspace!,
|
||||||
iconSize: workspace!.icon.isNotEmpty == true ? 46 : 20,
|
iconSize: workspace!.icon.isNotEmpty == true ? 46 : 20,
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
|
figmaLineHeight: 46,
|
||||||
enableEdit: true,
|
enableEdit: true,
|
||||||
onSelected: (r) => context
|
onSelected: (r) => context
|
||||||
.read<WorkspaceSettingsBloc>()
|
.read<WorkspaceSettingsBloc>()
|
||||||
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
|
|
||||||
/// Renders a simple category taking a title and the list
|
/// Renders a simple category taking a title and the list
|
||||||
/// of children (settings) to be rendered.
|
/// of children (settings) to be rendered.
|
||||||
|
@ -5,7 +5,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
|
|
||||||
/// This is used to describe a settings input field
|
/// This is used to describe a settings input field
|
||||||
///
|
///
|
||||||
|
@ -16,7 +16,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:string_validator/string_validator.dart';
|
import 'package:string_validator/string_validator.dart';
|
||||||
|
@ -18,7 +18,6 @@ import 'package:flowy_infra/size.dart';
|
|||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class SettingSupabaseCloudView extends StatelessWidget {
|
class SettingSupabaseCloudView extends StatelessWidget {
|
||||||
|
@ -15,7 +15,6 @@ import 'package:flowy_infra/file_picker/file_picker_impl.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
|
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import 'package:appflowy/workspace/presentation/widgets/rename_view_popover.dart
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -173,7 +172,7 @@ class _ViewTitleState extends State<_ViewTitle> {
|
|||||||
return Container(
|
return Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 6.0),
|
margin: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||||
child: _buildIconAndName(state, false),
|
child: _buildIconAndName(context, state, false),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +184,7 @@ class _ViewTitleState extends State<_ViewTitle> {
|
|||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
useIntrinsicWidth: true,
|
useIntrinsicWidth: true,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 6.0),
|
margin: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||||
text: _buildIconAndName(state, false),
|
text: _buildIconAndName(context, state, false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -216,13 +215,18 @@ class _ViewTitleState extends State<_ViewTitle> {
|
|||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
useIntrinsicWidth: true,
|
useIntrinsicWidth: true,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 6.0),
|
margin: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||||
text: _buildIconAndName(state, true),
|
text: _buildIconAndName(context, state, true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildIconAndName(ViewTitleState state, bool isEditable) {
|
Widget _buildIconAndName(
|
||||||
|
BuildContext context,
|
||||||
|
ViewTitleState state,
|
||||||
|
bool isEditable,
|
||||||
|
) {
|
||||||
|
final spaceIcon = state.view?.buildSpaceIconSvg(context);
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -234,10 +238,10 @@ class _ViewTitleState extends State<_ViewTitle> {
|
|||||||
),
|
),
|
||||||
const HSpace(4.0),
|
const HSpace(4.0),
|
||||||
],
|
],
|
||||||
if (state.view?.isSpace == true &&
|
if (state.view?.isSpace == true && spaceIcon != null) ...[
|
||||||
state.view?.spaceIconSvg != null) ...[
|
|
||||||
SpaceIcon(
|
SpaceIcon(
|
||||||
dimension: 14,
|
dimension: 14,
|
||||||
|
svgSize: 8.5,
|
||||||
space: state.view!,
|
space: state.view!,
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Basis
|
// Basis
|
||||||
|
export '/widget/flowy_tooltip.dart';
|
||||||
export '/widget/separated_flex.dart';
|
export '/widget/separated_flex.dart';
|
||||||
export '/widget/spacing.dart';
|
export '/widget/spacing.dart';
|
||||||
export 'basis.dart';
|
export 'basis.dart';
|
||||||
|
@ -7,7 +7,6 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flowy_svg/flowy_svg.dart';
|
import 'package:flowy_svg/flowy_svg.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
@ -24,8 +24,29 @@ class FlowySvg extends StatelessWidget {
|
|||||||
this.color,
|
this.color,
|
||||||
this.blendMode = BlendMode.srcIn,
|
this.blendMode = BlendMode.srcIn,
|
||||||
this.opacity = 1.0,
|
this.opacity = 1.0,
|
||||||
|
this.svgString,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Construct a FlowySvg Widget from a string
|
||||||
|
factory FlowySvg.string(
|
||||||
|
String svgString, {
|
||||||
|
Key? key,
|
||||||
|
Size? size,
|
||||||
|
Color? color,
|
||||||
|
BlendMode? blendMode = BlendMode.srcIn,
|
||||||
|
double opacity = 1.0,
|
||||||
|
}) {
|
||||||
|
return FlowySvg(
|
||||||
|
const FlowySvgData(''),
|
||||||
|
key: key,
|
||||||
|
size: size,
|
||||||
|
color: color,
|
||||||
|
blendMode: blendMode,
|
||||||
|
opacity: opacity,
|
||||||
|
svgString: svgString,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// The data for the flowy svg. Will be generated by the generator in this
|
/// The data for the flowy svg. Will be generated by the generator in this
|
||||||
/// package within bin/flowy_svg.dart
|
/// package within bin/flowy_svg.dart
|
||||||
final FlowySvgData svg;
|
final FlowySvgData svg;
|
||||||
@ -33,6 +54,9 @@ class FlowySvg extends StatelessWidget {
|
|||||||
/// The size of the svg
|
/// The size of the svg
|
||||||
final Size? size;
|
final Size? size;
|
||||||
|
|
||||||
|
/// The svg string
|
||||||
|
final String? svgString;
|
||||||
|
|
||||||
/// The color of the svg.
|
/// The color of the svg.
|
||||||
///
|
///
|
||||||
/// This property will not be applied to the underlying svg widget if the
|
/// This property will not be applied to the underlying svg widget if the
|
||||||
@ -57,12 +81,23 @@ class FlowySvg extends StatelessWidget {
|
|||||||
final iconColor =
|
final iconColor =
|
||||||
(color ?? Theme.of(context).iconTheme.color)?.withOpacity(opacity);
|
(color ?? Theme.of(context).iconTheme.color)?.withOpacity(opacity);
|
||||||
final textScaleFactor = MediaQuery.textScalerOf(context).scale(1);
|
final textScaleFactor = MediaQuery.textScalerOf(context).scale(1);
|
||||||
return Transform.scale(
|
|
||||||
scale: textScaleFactor,
|
final Widget svg;
|
||||||
child: SizedBox(
|
|
||||||
|
if (svgString != null) {
|
||||||
|
svg = SvgPicture.string(
|
||||||
|
svgString!,
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
height: size?.height,
|
height: size?.height,
|
||||||
child: SvgPicture.asset(
|
colorFilter: iconColor != null && blendMode != null
|
||||||
|
? ColorFilter.mode(
|
||||||
|
iconColor,
|
||||||
|
blendMode!,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
svg = SvgPicture.asset(
|
||||||
_normalized(),
|
_normalized(),
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
height: size?.height,
|
height: size?.height,
|
||||||
@ -72,7 +107,15 @@ class FlowySvg extends StatelessWidget {
|
|||||||
blendMode!,
|
blendMode!,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Transform.scale(
|
||||||
|
scale: textScaleFactor,
|
||||||
|
child: SizedBox(
|
||||||
|
width: size?.width,
|
||||||
|
height: size?.height,
|
||||||
|
child: svg,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -740,8 +740,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "4a5cac"
|
ref: "38c2c42"
|
||||||
resolved-ref: "4a5cac57e31c0ffd49cd6257a9e078f084ae342c"
|
resolved-ref: "38c2c429212af6b72a0af829bb0dd3f3eb4ce2c7"
|
||||||
url: "https://github.com/LucasXu0/emoji_mart.git"
|
url: "https://github.com/LucasXu0/emoji_mart.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
@ -111,7 +111,7 @@ dependencies:
|
|||||||
flutter_emoji_mart:
|
flutter_emoji_mart:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/LucasXu0/emoji_mart.git
|
url: https://github.com/LucasXu0/emoji_mart.git
|
||||||
ref: "4a5cac"
|
ref: "38c2c42"
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
# TODO: Consider implementing custom package
|
# TODO: Consider implementing custom package
|
||||||
@ -283,6 +283,7 @@ flutter:
|
|||||||
- assets/images/emoji/
|
- assets/images/emoji/
|
||||||
- assets/images/login/
|
- assets/images/login/
|
||||||
- assets/translations/
|
- assets/translations/
|
||||||
|
- assets/icons/icons.json
|
||||||
|
|
||||||
# The following assets will be excluded in release.
|
# The following assets will be excluded in release.
|
||||||
# BEGIN: EXCLUDE_IN_RELEASE
|
# BEGIN: EXCLUDE_IN_RELEASE
|
||||||
|
Loading…
Reference in New Issue
Block a user