diff --git a/.vs/AppFlowy/FileContentIndex/520dbd8f-66b9-4c8d-b60b-2460efd374b9.vsidx b/.vs/AppFlowy/FileContentIndex/520dbd8f-66b9-4c8d-b60b-2460efd374b9.vsidx new file mode 100644 index 0000000000000..70b4060b9ea96 Binary files /dev/null and b/.vs/AppFlowy/FileContentIndex/520dbd8f-66b9-4c8d-b60b-2460efd374b9.vsidx differ diff --git a/.vs/AppFlowy/FileContentIndex/5ac79d46-a185-4233-8a8a-a92af82f2ae9.vsidx b/.vs/AppFlowy/FileContentIndex/5ac79d46-a185-4233-8a8a-a92af82f2ae9.vsidx new file mode 100644 index 0000000000000..dc1670d3b1089 Binary files /dev/null and b/.vs/AppFlowy/FileContentIndex/5ac79d46-a185-4233-8a8a-a92af82f2ae9.vsidx differ diff --git a/.vs/AppFlowy/FileContentIndex/865ffcf8-137c-4643-9b43-8bc3f8e64f63.vsidx b/.vs/AppFlowy/FileContentIndex/865ffcf8-137c-4643-9b43-8bc3f8e64f63.vsidx new file mode 100644 index 0000000000000..c014f92f67d48 Binary files /dev/null and b/.vs/AppFlowy/FileContentIndex/865ffcf8-137c-4643-9b43-8bc3f8e64f63.vsidx differ diff --git a/.vs/AppFlowy/v17/.wsuo b/.vs/AppFlowy/v17/.wsuo new file mode 100644 index 0000000000000..1cc0dbe2b9cff Binary files /dev/null and b/.vs/AppFlowy/v17/.wsuo differ diff --git a/.vs/AppFlowy/v17/DocumentLayout.json b/.vs/AppFlowy/v17/DocumentLayout.json new file mode 100644 index 0000000000000..754942521441d --- /dev/null +++ b/.vs/AppFlowy/v17/DocumentLayout.json @@ -0,0 +1,12 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\Chithra\\AppFlowy\\", + "Documents": [], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [] + } + ] +} \ No newline at end of file diff --git a/.vs/AppFlowy/v17/workspaceFileList.bin b/.vs/AppFlowy/v17/workspaceFileList.bin new file mode 100644 index 0000000000000..6baee5ba501ea Binary files /dev/null and b/.vs/AppFlowy/v17/workspaceFileList.bin differ diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000000000..e44b20336fef8 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,8 @@ +{ + "ExpandedNodes": [ + "", + "\\frontend" + ], + "SelectedNode": "\\frontend", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000000000..abb3cdf129b5f Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/frontend/appflowy_flutter/lib/workspace/application/tag/tag.dart b/frontend/appflowy_flutter/lib/workspace/application/tag/tag.dart new file mode 100644 index 0000000000000..cb260d358eba5 --- /dev/null +++ b/frontend/appflowy_flutter/lib/workspace/application/tag/tag.dart @@ -0,0 +1,11 @@ +class Tag { + final String id; + final String name; + final int color; + + const Tag({ + required this.id, + required this.name, + required this.color, + }); +} diff --git a/frontend/appflowy_flutter/lib/workspace/application/tag/tag_filter.dart b/frontend/appflowy_flutter/lib/workspace/application/tag/tag_filter.dart new file mode 100644 index 0000000000000..161c03832e6cf --- /dev/null +++ b/frontend/appflowy_flutter/lib/workspace/application/tag/tag_filter.dart @@ -0,0 +1,13 @@ +import 'tag.dart'; + +class TagFilter { + static bool match({ + required List tags, + required String query, + }) { + if (query.isEmpty) return true; + + final lower = query.toLowerCase(); + return tags.any((tag) => tag.name.toLowerCase().contains(lower)); + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart index 18f56d4e8bc4a..028fff294b9e7 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart @@ -29,9 +29,11 @@ import 'package:provider/provider.dart'; import 'package:time/time.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:window_manager/window_manager.dart'; - import 'home_layout.dart'; +import '../widgets/tags/tag_search_bar.dart'; + + typedef NavigationCallback = void Function(String id); abstract class HomeStackDelegate { @@ -80,6 +82,17 @@ class _HomeStackState extends State with WindowListener { }, ), ), + + //tag feature + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TagSearchBar( + onChanged: (query) { + // TEMP: just prints text + debugPrint('Search query: $query'); + }, + ), + ), Expanded( child: IndexedStack( index: selectedIndex, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/tags/tag_editor.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/tags/tag_editor.dart new file mode 100644 index 0000000000000..337579cbdea87 --- /dev/null +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/tags/tag_editor.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import '../../../application/tag/tag.dart'; + +class TagEditor extends StatefulWidget { + final List tags; + final ValueChanged> onChanged; + + const TagEditor({ + super.key, + required this.tags, + required this.onChanged, + }); + + @override + State createState() => _TagEditorState(); +} + +class _TagEditorState extends State { + final controller = TextEditingController(); + + void _addTag(String value) { + if (value.trim().isEmpty) return; + + widget.onChanged([ + ...widget.tags, + Tag( + id: DateTime.now().millisecondsSinceEpoch.toString(), + name: value.trim(), + color: Colors.blue.value, + ), + ]); + + controller.clear(); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: 6, + children: widget.tags.map((tag) { + return Chip( + label: Text(tag.name), + onDeleted: () { + widget.onChanged( + widget.tags.where((t) => t.id != tag.id).toList(), + ); + }, + ); + }).toList(), + ), + const SizedBox(height: 6), + TextField( + controller: controller, + onSubmitted: _addTag, + decoration: const InputDecoration( + hintText: 'Add tag', + isDense: true, + ), + ), + ], + ); + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/tags/tag_search_bar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/tags/tag_search_bar.dart new file mode 100644 index 0000000000000..ece65a0496860 --- /dev/null +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/tags/tag_search_bar.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class TagSearchBar extends StatelessWidget { + final ValueChanged onChanged; + + const TagSearchBar({ + super.key, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8), + child: TextField( + decoration: const InputDecoration( + hintText: 'Search by tag...', + prefixIcon: Icon(Icons.search), + border: OutlineInputBorder(), + ), + onChanged: onChanged, + ), + ); + } +}