mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: 0.3.8 known issues (#3912)
* fix: add a left padding to align the document and grid field
* fix: emoji picker in the slash menu is too small
* fix: replace the delete icon color with black
* fix: improve snackbar background color
* fix: cannot add new line after toggle list
* feat: set ⭐ as the default icon of getting started
* fix: the titlebar overflows when the title level is too deep
* fix: integration test
* fix: openAI hint text overflow
* fix: integration tests
This commit is contained in:
parent
7cee8e392f
commit
251c6d22b2
@ -22,7 +22,6 @@ void main() {
|
||||
|
||||
// Hover over cover toolbar to show 'Add Cover' and 'Add Icon' buttons
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
tester.expectToSeePluginAddCoverAndIconButton();
|
||||
|
||||
// Insert a document cover
|
||||
await tester.editor.tapOnAddCover();
|
||||
@ -58,14 +57,10 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
tester.expectToSeeDocumentIcon(null);
|
||||
|
||||
// Hover over cover toolbar to show the 'Add Cover' and 'Add Icon' buttons
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
tester.expectToSeePluginAddCoverAndIconButton();
|
||||
tester.expectToSeeDocumentIcon('⭐️');
|
||||
|
||||
// Insert a document icon
|
||||
await tester.editor.tapAddIconButton();
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
await tester.tapEmoji('😀');
|
||||
tester.expectToSeeDocumentIcon('😀');
|
||||
|
||||
@ -95,18 +90,15 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
tester.expectToSeeDocumentIcon(null);
|
||||
tester.expectToSeeDocumentIcon('⭐️');
|
||||
tester.expectToSeeNoDocumentCover();
|
||||
|
||||
// Hover over cover toolbar to show the 'Add Cover' and 'Add Icon' buttons
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
tester.expectToSeePluginAddCoverAndIconButton();
|
||||
|
||||
// Insert a document icon
|
||||
await tester.editor.tapAddIconButton();
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Insert a document cover
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
await tester.editor.tapOnAddCover();
|
||||
|
||||
// Expect to see the icon and cover at the same time
|
||||
@ -122,8 +114,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
await tester.editor.tapAddIconButton();
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
|
||||
// click the shuffle button
|
||||
await tester.tapButton(
|
||||
@ -136,8 +127,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
await tester.editor.tapAddIconButton();
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
|
||||
final searchEmojiTextField = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
|
@ -60,6 +60,15 @@ class EditorOperations {
|
||||
expect(find.byType(FlowyEmojiPicker), findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> tapGettingStartedIcon() async {
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(DocumentHeaderNodeWidget),
|
||||
matching: find.findTextInFlowyText('⭐️'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Taps on the 'Skin tone' button
|
||||
///
|
||||
/// Must call [tapAddIconButton] first.
|
||||
@ -67,7 +76,7 @@ class EditorOperations {
|
||||
await tester.tapButton(
|
||||
find.byTooltip(LocaleKeys.emoji_selectSkinTone.tr()),
|
||||
);
|
||||
final skinToneButton = find.text(EmojiSkinToneWrapper(skinTone).name);
|
||||
final skinToneButton = find.byKey(emojiSkinToneKey(skinTone.icon));
|
||||
await tester.tapButton(skinToneButton);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
// const String readme = 'Read me';
|
||||
const String gettingStarted = '⭐️ Getting started';
|
||||
const String gettingStarted = 'Getting started';
|
||||
|
||||
extension Expectation on WidgetTester {
|
||||
/// Expect to see the home page and with a default read me page.
|
||||
|
@ -24,7 +24,6 @@ class BottomSheetActionWidget extends StatelessWidget {
|
||||
icon: FlowySvg(
|
||||
svg,
|
||||
size: const Size.square(22.0),
|
||||
blendMode: BlendMode.dst,
|
||||
color: iconColor,
|
||||
),
|
||||
label: Text(text),
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:emoji_mart/emoji_mart.dart';
|
||||
@ -10,6 +9,11 @@ import 'package:flutter/material.dart';
|
||||
// use a temporary global value to store last selected skin tone
|
||||
EmojiSkinTone? lastSelectedEmojiSkinTone;
|
||||
|
||||
@visibleForTesting
|
||||
ValueKey emojiSkinToneKey(String icon) {
|
||||
return ValueKey('emoji_skin_tone_$icon');
|
||||
}
|
||||
|
||||
class FlowyEmojiSkinToneSelector extends StatefulWidget {
|
||||
const FlowyEmojiSkinToneSelector({
|
||||
super.key,
|
||||
@ -26,73 +30,59 @@ class FlowyEmojiSkinToneSelector extends StatefulWidget {
|
||||
class _FlowyEmojiSkinToneSelectorState
|
||||
extends State<FlowyEmojiSkinToneSelector> {
|
||||
EmojiSkinTone skinTone = EmojiSkinTone.none;
|
||||
final controller = PopoverController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopoverActionList<EmojiSkinToneWrapper>(
|
||||
return AppFlowyPopover(
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
offset: const Offset(0, 8),
|
||||
actions: EmojiSkinTone.values
|
||||
.map((action) => EmojiSkinToneWrapper(action))
|
||||
.toList(),
|
||||
buildChild: (controller) {
|
||||
return FlowyTooltip(
|
||||
message: LocaleKeys.emoji_selectSkinTone.tr(),
|
||||
child: FlowyIconButton(
|
||||
icon: Padding(
|
||||
// add a left padding to align the emoji center
|
||||
padding: const EdgeInsets.only(
|
||||
left: 3.0,
|
||||
),
|
||||
child: FlowyText(
|
||||
lastSelectedEmojiSkinTone?.icon ?? '✋',
|
||||
fontSize: 22.0,
|
||||
),
|
||||
),
|
||||
onPressed: () => controller.show(),
|
||||
),
|
||||
controller: controller,
|
||||
popupBuilder: (context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: EmojiSkinTone.values
|
||||
.map(
|
||||
(e) => _buildIconButton(
|
||||
e.icon,
|
||||
() {
|
||||
setState(() => lastSelectedEmojiSkinTone = e);
|
||||
widget.onEmojiSkinToneChanged(e);
|
||||
controller.close();
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
onSelected: (action, controller) async {
|
||||
widget.onEmojiSkinToneChanged(action.inner);
|
||||
setState(() {
|
||||
lastSelectedEmojiSkinTone = action.inner;
|
||||
});
|
||||
controller.close();
|
||||
},
|
||||
child: FlowyTooltip(
|
||||
message: LocaleKeys.emoji_selectSkinTone.tr(),
|
||||
child: _buildIconButton(
|
||||
lastSelectedEmojiSkinTone?.icon ?? '✋',
|
||||
() => controller.show(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIconButton(String icon, VoidCallback onPressed) {
|
||||
return FlowyIconButton(
|
||||
key: emojiSkinToneKey(icon),
|
||||
icon: Padding(
|
||||
// add a left padding to align the emoji center
|
||||
padding: const EdgeInsets.only(
|
||||
left: 3.0,
|
||||
),
|
||||
child: FlowyText(
|
||||
icon,
|
||||
fontSize: 22.0,
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EmojiSkinToneWrapper extends ActionCell {
|
||||
EmojiSkinToneWrapper(this.inner);
|
||||
|
||||
final EmojiSkinTone inner;
|
||||
|
||||
Widget? icon(Color iconColor) => null;
|
||||
|
||||
@override
|
||||
String get name {
|
||||
final String i18n;
|
||||
switch (inner) {
|
||||
case EmojiSkinTone.none:
|
||||
i18n = LocaleKeys.emoji_skinTone_default.tr();
|
||||
case EmojiSkinTone.light:
|
||||
i18n = LocaleKeys.emoji_skinTone_light.tr();
|
||||
case EmojiSkinTone.mediumLight:
|
||||
i18n = LocaleKeys.emoji_skinTone_mediumLight.tr();
|
||||
case EmojiSkinTone.medium:
|
||||
i18n = LocaleKeys.emoji_skinTone_medium.tr();
|
||||
case EmojiSkinTone.mediumDark:
|
||||
i18n = LocaleKeys.emoji_skinTone_mediumDark.tr();
|
||||
case EmojiSkinTone.dark:
|
||||
i18n = LocaleKeys.emoji_skinTone_dark.tr();
|
||||
}
|
||||
return '${inner.icon} $i18n';
|
||||
}
|
||||
}
|
||||
|
||||
extension on EmojiSkinTone {
|
||||
extension EmojiSkinToneIcon on EmojiSkinTone {
|
||||
String get icon {
|
||||
switch (this) {
|
||||
case EmojiSkinTone.none:
|
||||
|
@ -123,7 +123,7 @@ class _RowEditorState extends State<RowEditor> {
|
||||
scrollController: widget.scrollController,
|
||||
styleCustomizer: EditorStyleCustomizer(
|
||||
context: context,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
padding: const EdgeInsets.only(left: 16, right: 54),
|
||||
),
|
||||
showParagraphPlaceholder: (editorState, node) =>
|
||||
editorState.document.isEmpty,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/build_context_extension.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/text_robot.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||
@ -7,6 +8,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/wid
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
@ -15,8 +17,6 @@ import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AutoCompletionBlockKeys {
|
||||
@ -169,9 +169,12 @@ class _AutoCompletionBlockComponentState
|
||||
return FlowyTextField(
|
||||
hintText: LocaleKeys.document_plugins_autoGeneratorHintText.tr(),
|
||||
controller: controller,
|
||||
maxLines: 3,
|
||||
maxLines: 5,
|
||||
focusNode: textFieldFocusNode,
|
||||
autoFocus: false,
|
||||
hintTextConstraints: const BoxConstraints(
|
||||
maxHeight: double.infinity,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -70,9 +70,12 @@ CharacterShortcutEvent insertChildNodeInsideToggleList = CharacterShortcutEvent(
|
||||
// insert a toggle list block below the current toggle list block
|
||||
transaction
|
||||
..deleteText(node, selection.startIndex, slicedDelta.length)
|
||||
..insertNode(
|
||||
..insertNodes(
|
||||
selection.start.path.next,
|
||||
toggleListBlockNode(collapsed: true, delta: slicedDelta),
|
||||
[
|
||||
toggleListBlockNode(collapsed: true, delta: slicedDelta),
|
||||
paragraphNode(),
|
||||
],
|
||||
)
|
||||
..afterSelection = Selection.collapsed(
|
||||
Position(path: selection.start.path.next, offset: 0),
|
||||
|
@ -106,16 +106,20 @@ extension ViewExtension on ViewPB {
|
||||
|
||||
FlowySvgData get iconData => layout.icon;
|
||||
|
||||
Future<List<ViewPB>> getAncestors({bool includeSelf = false}) async {
|
||||
Future<List<ViewPB>> getAncestors({
|
||||
bool includeSelf = false,
|
||||
bool includeRoot = false,
|
||||
}) async {
|
||||
final ancestors = <ViewPB>[];
|
||||
if (includeSelf) {
|
||||
ancestors.add(this);
|
||||
final self = await ViewBackendService.getView(id);
|
||||
ancestors.add(self.getLeftOrNull<ViewPB>() ?? this);
|
||||
}
|
||||
var parent = await ViewBackendService.getView(parentViewId);
|
||||
while (parent.isLeft()) {
|
||||
// parent is not null
|
||||
final view = parent.getLeftOrNull<ViewPB>();
|
||||
if (view == null) {
|
||||
if (view == null || (!includeRoot && view.parentViewId.isEmpty)) {
|
||||
break;
|
||||
}
|
||||
ancestors.add(view);
|
||||
|
@ -18,6 +18,12 @@ class SidebarFolder extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// check if there is any duplicate views
|
||||
final views = this.views.toSet().toList();
|
||||
final favoriteViews = this.favoriteViews.toSet().toList();
|
||||
assert(views.length == this.views.length);
|
||||
assert(favoriteViews.length == favoriteViews.length);
|
||||
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: getIt<MenuSharedState>().notifier,
|
||||
builder: (context, value, child) {
|
||||
@ -27,6 +33,7 @@ class SidebarFolder extends StatelessWidget {
|
||||
// favorite
|
||||
if (favoriteViews.isNotEmpty) ...[
|
||||
FavoriteFolder(
|
||||
// remove the duplicate views
|
||||
views: favoriteViews,
|
||||
),
|
||||
const VSpace(10),
|
||||
|
@ -55,6 +55,7 @@ void showSnackBarMessage(
|
||||
}) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.onSecondary,
|
||||
action: !showCancel
|
||||
? null
|
||||
: SnackBarAction(
|
||||
@ -66,7 +67,6 @@ void showSnackBarMessage(
|
||||
),
|
||||
content: FlowyText(
|
||||
message,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -44,8 +44,8 @@ void showEmojiPickerMenu(
|
||||
builder: (context) => Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Container(
|
||||
width: 300,
|
||||
height: 250,
|
||||
width: 360,
|
||||
height: 380,
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
decoration: FlowyDecoration.decoration(
|
||||
Theme.of(context).cardColor,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
|
||||
import 'package:appflowy/startup/tasks/app_window_size_manager.dart';
|
||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||
@ -29,9 +30,7 @@ class _ViewTitleBarState extends State<ViewTitleBar> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
ancestors = widget.view.getAncestors(
|
||||
includeSelf: true,
|
||||
);
|
||||
_reloadAncestors();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -39,9 +38,7 @@ class _ViewTitleBarState extends State<ViewTitleBar> {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (oldWidget.view.id != widget.view.id) {
|
||||
ancestors = widget.view.getAncestors(
|
||||
includeSelf: true,
|
||||
);
|
||||
_reloadAncestors();
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,27 +48,57 @@ class _ViewTitleBarState extends State<ViewTitleBar> {
|
||||
future: ancestors,
|
||||
builder: ((context, snapshot) {
|
||||
final ancestors = snapshot.data;
|
||||
if (ancestors == null ||
|
||||
snapshot.connectionState != ConnectionState.done) {
|
||||
if (ancestors == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Row(
|
||||
const maxWidth = WindowSizeManager.minWindowWidth - 100;
|
||||
final replacement = Row(
|
||||
// refresh the view title bar when the ancestors changed
|
||||
key: ValueKey(ancestors.hashCode),
|
||||
children: _buildViewTitles(ancestors),
|
||||
);
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Visibility(
|
||||
visible: constraints.maxWidth < maxWidth,
|
||||
replacement: replacement,
|
||||
// if the width is too small, only show one view title bar without the ancestors
|
||||
child: _ViewTitle(
|
||||
view: ancestors.last,
|
||||
behavior: _ViewTitleBehavior.editable,
|
||||
maxTitleWidth: constraints.maxWidth - 50.0,
|
||||
onUpdated: () => setState(() => _reloadAncestors()),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildViewTitles(List<ViewPB> views) {
|
||||
// if the level is too deep, only show the last two view, the first one view and the root view
|
||||
bool hasAddedEllipsis = false;
|
||||
final children = <Widget>[];
|
||||
|
||||
for (var i = 0; i < views.length; i++) {
|
||||
final view = views[i];
|
||||
if (i >= 1 && i < views.length - 2) {
|
||||
if (!hasAddedEllipsis) {
|
||||
hasAddedEllipsis = true;
|
||||
children.add(
|
||||
const FlowyText.regular(' ... /'),
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
children.add(
|
||||
_ViewTitle(
|
||||
view: view,
|
||||
behavior: i == views.length - 1
|
||||
? _ViewTitleBehavior.editable // only the last one is editable
|
||||
: _ViewTitleBehavior.uneditable, // others are not editable
|
||||
onUpdated: () => setState(() => _reloadAncestors()),
|
||||
),
|
||||
);
|
||||
if (i != views.length - 1) {
|
||||
@ -81,6 +108,12 @@ class _ViewTitleBarState extends State<ViewTitleBar> {
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
void _reloadAncestors() {
|
||||
ancestors = widget.view.getAncestors(
|
||||
includeSelf: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum _ViewTitleBehavior {
|
||||
@ -92,10 +125,14 @@ class _ViewTitle extends StatefulWidget {
|
||||
const _ViewTitle({
|
||||
required this.view,
|
||||
this.behavior = _ViewTitleBehavior.editable,
|
||||
this.maxTitleWidth = 180,
|
||||
required this.onUpdated,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final _ViewTitleBehavior behavior;
|
||||
final double maxTitleWidth;
|
||||
final VoidCallback onUpdated;
|
||||
|
||||
@override
|
||||
State<_ViewTitle> createState() => _ViewTitleState();
|
||||
@ -124,6 +161,7 @@ class _ViewTitleState extends State<_ViewTitle> {
|
||||
icon = view.icon.value;
|
||||
_resetTextEditingController();
|
||||
});
|
||||
widget.onUpdated();
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -156,7 +194,15 @@ class _ViewTitleState extends State<_ViewTitle> {
|
||||
fontSize: 18.0,
|
||||
),
|
||||
const HSpace(2.0),
|
||||
FlowyText.regular(name),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: widget.maxTitleWidth,
|
||||
),
|
||||
child: FlowyText.regular(
|
||||
name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@ -188,8 +234,8 @@ class _ViewTitleState extends State<_ViewTitle> {
|
||||
defaultIcon: widget.view.defaultIcon(),
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
offset: const Offset(0, 18),
|
||||
onSubmitted: (emoji, _) {
|
||||
ViewBackendService.updateViewIcon(
|
||||
onSubmitted: (emoji, _) async {
|
||||
await ViewBackendService.updateViewIcon(
|
||||
viewId: widget.view.id,
|
||||
viewIcon: emoji,
|
||||
);
|
||||
@ -203,9 +249,9 @@ class _ViewTitleState extends State<_ViewTitle> {
|
||||
child: FlowyTextField(
|
||||
autoFocus: true,
|
||||
controller: textEditingController,
|
||||
onSubmitted: (text) {
|
||||
onSubmitted: (text) async {
|
||||
if (text.isNotEmpty && text != name) {
|
||||
ViewBackendService.updateView(
|
||||
await ViewBackendService.updateView(
|
||||
viewId: widget.view.id,
|
||||
name: text,
|
||||
);
|
||||
|
@ -26,6 +26,7 @@ class FlowyTextField extends StatefulWidget {
|
||||
final Widget? suffixIcon;
|
||||
final BoxConstraints? prefixIconConstraints;
|
||||
final BoxConstraints? suffixIconConstraints;
|
||||
final BoxConstraints? hintTextConstraints;
|
||||
|
||||
const FlowyTextField({
|
||||
super.key,
|
||||
@ -50,6 +51,7 @@ class FlowyTextField extends StatefulWidget {
|
||||
this.suffixIcon,
|
||||
this.prefixIconConstraints,
|
||||
this.suffixIconConstraints,
|
||||
this.hintTextConstraints,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -121,15 +123,22 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||
},
|
||||
onSubmitted: (text) => _onSubmitted(text),
|
||||
onEditingComplete: widget.onEditingComplete,
|
||||
minLines: 1,
|
||||
maxLines: widget.maxLines,
|
||||
maxLength: widget.maxLength,
|
||||
maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds,
|
||||
style: widget.textStyle ?? Theme.of(context).textTheme.bodySmall,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
keyboardType: TextInputType.multiline,
|
||||
decoration: InputDecoration(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: widget.errorText?.isEmpty ?? true ? 32 : 58),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
constraints: widget.hintTextConstraints ??
|
||||
BoxConstraints(
|
||||
maxHeight: widget.errorText?.isEmpty ?? true ? 32 : 58,
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: widget.maxLines > 1 ? 12 : 0,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
|
@ -101,11 +101,14 @@ impl FolderOperationHandler for DocumentFolderOperation {
|
||||
FutureResult::new(async move {
|
||||
let mut write_guard = workspace_view_builder.write().await;
|
||||
|
||||
// Create a view named "⭐️ Getting started" with built-in README data.
|
||||
// Create a view named "Getting started" with an icon ⭐️ and the built-in README data.
|
||||
// Don't modify this code unless you know what you are doing.
|
||||
write_guard
|
||||
.with_view_builder(|view_builder| async {
|
||||
let view = view_builder.with_name("⭐️ Getting started").build();
|
||||
let view = view_builder
|
||||
.with_name("Getting started")
|
||||
.with_icon("⭐️")
|
||||
.build();
|
||||
// create a empty document
|
||||
let json_str = include_str!("../../assets/read_me.json");
|
||||
let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap();
|
||||
|
@ -96,6 +96,14 @@ impl ViewBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_icon(mut self, icon: &str) -> Self {
|
||||
self.icon = Some(ViewIcon {
|
||||
ty: collab_folder::IconType::Emoji,
|
||||
value: icon.to_string(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a child view for the current view.
|
||||
/// The view created by this builder will be the next level view of the current view.
|
||||
pub async fn with_child_view_builder<F, O>(mut self, child_view_builder: F) -> Self
|
||||
|
Loading…
Reference in New Issue
Block a user