Добавьте файлы проекта.

This commit is contained in:
ARCHI86 2024-11-02 19:06:36 +03:00
parent cf497cd298
commit a2c4204b1f
63 changed files with 3081 additions and 0 deletions

14
App.xaml Normal file
View file

@ -0,0 +1,14 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NotesDataAnalyst.NoteContent"
x:Class="NotesDataAnalyst.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="/Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

37
App.xaml.cs Normal file
View file

@ -0,0 +1,37 @@

using NotesDataAnalyst.NoteContent;
namespace NotesDataAnalyst
{
public partial class App : Application
{
public static List<Note> AllNotes { get; set; } = new List<Note>();
public static DatabaseContext Database { get; private set; }
public App()
{
InitializeComponent();
string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "notes.db");
Database = new DatabaseContext(dbPath);
MainPage = new AppShell();
}
// Метод для добавления или сохранения заметки
public static void SaveNote(Note note)
{
var existingNote = AllNotes.FirstOrDefault(n => n.Id == note.Id);
if (existingNote == null)
{
AllNotes.Add(note);
}
else
{
// Обновляем существующую заметку
existingNote.Title = note.Title;
existingNote.Text = note.Text;
}
}
}
}

18
AppShell.xaml Normal file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="NotesDataAnalyst.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NotesDataAnalyst"
Shell.FlyoutBehavior="Flyout"
Shell.NavBarIsVisible="True"
Title="NotesDataAnalyst">
<!-- Главная страница приложения -->
<FlyoutItem Title="Главная" Icon="add.png">
<ShellContent ContentTemplate="{DataTemplate local:GraphPage}" />
</FlyoutItem>
<!-- Выпадающий список категорий -->
<MenuItem Text="Категории" Clicked="OnCategoriesMenuClicked" />
</Shell>

74
AppShell.xaml.cs Normal file
View file

@ -0,0 +1,74 @@
namespace NotesDataAnalyst
{
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
CategoryManager.LoadCategories(); // Загружаем категории при запуске
// Регистрация маршрута для MainPage
Routing.RegisterRoute("MainPage", typeof(MainPage));
}
private async void OnCategoriesMenuClicked(object sender, EventArgs e)
{
// Открываем всплывающее окно с возможностью управления категориями
string action = await DisplayActionSheet("Управление категориями", "Отмена", null,
"Выбрать категорию", "Добавить категорию", "Удалить категорию");
switch (action)
{
case "Выбрать категорию":
await SelectCategory();
break;
case "Добавить категорию":
await AddCategory();
break;
case "Удалить категорию":
await DeleteCategory();
break;
}
}
private async Task SelectCategory()
{
// Всплывающее окно для выбора категории
string selectedCategory = await DisplayActionSheet("Выберите категорию", "Отмена", null, CategoryManager.Categories.ToArray());
if (!string.IsNullOrEmpty(selectedCategory) && selectedCategory != "Отмена")
{
// Устанавливаем выбранную категорию
CategoryManager.ActiveCategory = selectedCategory;
// Переход к MainPage относительно текущего стека, без абсолютного маршрута
await Shell.Current.GoToAsync("MainPage");
}
}
private async Task AddCategory()
{
// Всплывающее окно для ввода названия новой категории
string newCategory = await DisplayPromptAsync("Новая категория", "Введите название категории:");
if (!string.IsNullOrWhiteSpace(newCategory) && !CategoryManager.Categories.Contains(newCategory))
{
CategoryManager.AddCategory(newCategory);
}
}
private async Task DeleteCategory()
{
// Всплывающее окно для выбора категории для удаления
string categoryToDelete = await DisplayActionSheet("Удалить категорию", "Отмена", null, CategoryManager.Categories.ToArray());
if (!string.IsNullOrEmpty(categoryToDelete) && categoryToDelete != "Отмена")
{
// Удаляем категорию
CategoryManager.Categories.Remove(categoryToDelete);
// Сброс активной категории, если она была удалена
if (CategoryManager.ActiveCategory == categoryToDelete)
{
CategoryManager.ActiveCategory = "Все категории";
}
}
}
}
}

80
GraphDrawable.cs Normal file
View file

@ -0,0 +1,80 @@
using Microsoft.Maui.Graphics;
using System.Collections.Generic;
namespace NotesDataAnalyst
{
public class GraphDrawable : IDrawable
{
private readonly List<GraphNode> _nodes;
private readonly List<GraphConnection> _connections;
public GraphDrawable(List<GraphNode> nodes, List<GraphConnection> connections)
{
_nodes = nodes;
_connections = connections;
}
private void DrawConnection(ICanvas canvas, GraphConnection connection)
{
var startX = connection.FromNode.Position.X + connection.FromNode.Size.Width / 2;
var startY = connection.FromNode.Position.Y + connection.FromNode.Size.Height / 2;
var endX = connection.ToNode.Position.X + connection.ToNode.Size.Width / 2;
var endY = connection.ToNode.Position.Y + connection.ToNode.Size.Height / 2;
canvas.StrokeColor = Colors.Gray;
canvas.StrokeSize = 2;
canvas.DrawLine((float)startX, (float)startY, (float)endX, (float)endY);
}
private void DrawNode(ICanvas canvas, GraphNode node)
{
canvas.FillColor = node.BackgroundColor;
canvas.FillRectangle((float)node.Position.X, (float)node.Position.Y, (float)node.Size.Width, (float)node.Size.Height);
canvas.FontColor = Colors.Black;
canvas.DrawString(node.Text, (float)node.Position.X + 10, (float)node.Position.Y + 20, HorizontalAlignment.Left);
}
public void Draw(ICanvas canvas, RectF dirtyRect)
{
canvas.StrokeColor = Colors.Black;
canvas.StrokeSize = 2;
// Отрисовка связей
foreach (var connection in _connections)
{
canvas.DrawLine(
(float)connection.FromNode.Position.X, (float)connection.FromNode.Position.Y,
(float)connection.ToNode.Position.X, (float)connection.ToNode.Position.Y);
}
// Отрисовка узлов
foreach (var node in _nodes)
{
// Заливка фона узла
canvas.FillColor = node.BackgroundColor;
canvas.FillRectangle((float)node.Position.X, (float)node.Position.Y, (float)node.Size.Width, (float)node.Size.Height);
// Отрисовка текста
canvas.FontColor = Colors.Black;
canvas.DrawString(node.Text, (float)node.Position.X + 5, (float)node.Position.Y + 5, HorizontalAlignment.Left);
// Если нода выбрана, рисуем пунктирную обводку и "ручку" для изменения размера
if (node.IsActive)
{
canvas.StrokeColor = Colors.Blue;
canvas.StrokeDashPattern = new float[] { 4, 2 };
canvas.DrawRectangle((float)node.Position.X, (float)node.Position.Y, (float)node.Size.Width, (float)node.Size.Height);
// Ручка для изменения размера
canvas.FillColor = Colors.Gray;
canvas.FillRectangle(
(float)(node.Position.X + node.Size.Width - 10),
(float)(node.Position.Y + node.Size.Height - 10),
10, 10);
}
}
}
}
}

25
GraphNode.cs Normal file
View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotesDataAnalyst
{
public class GraphNode
{
public bool IsActive { get; set; }
public string Id { get; } = Guid.NewGuid().ToString(); // Уникальный идентификатор
public string Text { get; set; }
public Color BackgroundColor { get; set; } = Colors.LightBlue;
public Point Position { get; set; } // Позиция узла
public Size Size { get; set; } = new Size(100, 50); // Размер узла
public List<GraphNode> Children { get; } = new List<GraphNode>(); // Список дочерних узлов
}
public class GraphConnection
{
public GraphNode FromNode { get; set; } // Узел начала связи
public GraphNode ToNode { get; set; } // Узел конца связи
}
}

18
GraphPage.xaml Normal file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NotesDataAnalyst.GraphPage"
Title="GraphPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add Node" Clicked="OnAddNodeClicked"/>
<ToolbarItem Text="Добавить дочернюю" Clicked="OnChildClicked"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<GraphicsView x:Name="canvasView"
HeightRequest="800"
WidthRequest="800" />
</ContentPage.Content>
</ContentPage>

310
GraphPage.xaml.cs Normal file
View file

@ -0,0 +1,310 @@
using System.Diagnostics;
using System.Linq;
namespace NotesDataAnalyst
{
public partial class GraphPage : ContentPage
{
private readonly Entry _textEditor; // Ïîëå äëÿ ââîäà òåêñòà
private PointF _initialNodePosition; // Õðàíèò íà÷àëüíóþ ïîçèöèþ íîäû
private PointF _initialTouchPosition; // Õðàíèò íà÷àëüíóþ ïîçèöèþ êàñàíèÿ
private SizeF _initialNodeSize; // Õðàíèò íà÷àëüíûé ðàçìåð íîäû
private readonly List<GraphNode> _nodes = new();
private readonly List<GraphConnection> _connections = new();
private GraphNode _selectedNode;
private bool _isResizing = false;
public GraphPage()
{
InitializeComponent();
// Ñîçäàåì ñêðûòîå ïîëå äëÿ ðåäàêòèðîâàíèÿ òåêñòà
_textEditor = new Entry
{
IsVisible = false,
BackgroundColor = Colors.Transparent,
FontSize = 18,
TextColor = Colors.Black
};
_textEditor.Completed += OnTextEditorCompleted; // Ñîõðàíÿåì òåêñò ïðè çàâåðøåíèè ââîäà
this.Add(_textEditor);
// Íàñòðàèâàåì Drawable äëÿ îòðèñîâêè óçëîâ è ñâÿçåé
var graphDrawable = new GraphDrawable(_nodes, _connections);
canvasView.Drawable = graphDrawable;
// Äîáàâëÿåì æåñòû
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += OnPanUpdated;
canvasView.GestureRecognizers.Add(panGesture);
var tapGesture = new TapGestureRecognizer();
tapGesture.Tapped += OnCanvasTapped;
canvasView.GestureRecognizers.Add(tapGesture);
}
private void OnNodeTapped(object sender, TappedEventArgs e)
{
var node = _selectedNode;
var tapPosition = e.GetPosition((View)sender);
if (tapPosition.HasValue)
{
var touchPoint = new PointF((float)tapPosition.Value.X, (float)tapPosition.Value.Y);
_selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node));
if (_selectedNode != null)
{
// Ïîçèöèîíèðóåì è ïîêàçûâàåì òåêñòîâîå ïîëå
_textEditor.Text = _selectedNode.Text;
_textEditor.IsVisible = true;
_textEditor.Focus();
AbsoluteLayout.SetLayoutBounds(_textEditor, new Rect(_selectedNode.Position.X + 5, _selectedNode.Position.Y + 5, _selectedNode.Size.Width - 10, 30));
}
}
Debug.WriteLine("Íàæàòà íîäà - {node}");
}
private void OnTextEditorCompleted(object sender, EventArgs e)
{
// Ñîõðàíÿåì òåêñò â íîäó
if (_selectedNode != null)
{
_selectedNode.Text = _textEditor.Text;
_textEditor.IsVisible = false;
canvasView.Invalidate();
}
}
private void OnChildClicked(object sender, EventArgs e)
{
if (_selectedNode != null)
{
var child = new GraphNode
{
Text = "Child Node",
Position = new Point(_selectedNode.Position.X + 5, _selectedNode.Position.Y + 5),
Size = _selectedNode.Size,
BackgroundColor = Colors.Gray,
};
_nodes.Add(child);
_connections.Add(new GraphConnection { FromNode = _selectedNode, ToNode = child });
canvasView.Invalidate();
}
else { Debug.WriteLine("Ñíà÷àëà âûáèðèòå íîäó"); }
}
private async void OnAddNodeClicked(object sender, EventArgs e)
{
var newNode = new GraphNode
{
Text = "Íîâàÿ íîäà",
Position = new Point(300, 200),
Size = new Size(200, 200),
};
_nodes.Add(newNode);
canvasView.Invalidate();
}
// Îáðàáîò÷èê äëÿ âûáîðà óçëà
// Îáðàáîò÷èê äëÿ âûáîðà óçëà
private void OnCanvasTapped(object sender, TappedEventArgs e)
{
var tapPosition = e.GetPosition((View)sender);
_selectedNode = null;
if (tapPosition.HasValue)
{
var touchPoint = new PointF((float)tapPosition.Value.X, (float)tapPosition.Value.Y);
_selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node));
// Ñáðîñ àêòèâíîñòè äëÿ âñåõ óçëîâ, êðîìå âûáðàííîãî
foreach (var node in _nodes)
node.IsActive = node == _selectedNode;
// Îáíîâëÿåì ýêðàí ïîñëå èçìåíåíèÿ ñîñòîÿíèÿ óçëîâ
canvasView.Invalidate();
}
}
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
if (_selectedNode == null) return;
if (e.StatusType == GestureStatus.Started)
{
// Ñîõðàíÿåì íà÷àëüíóþ ïîçèöèþ íîäû è ðàçìåð
_initialNodePosition = _selectedNode.Position;
_initialTouchPosition = new PointF((float)e.TotalX, (float)e.TotalY);
_initialNodeSize = _selectedNode.Size; // Ñîõðàíÿåì íà÷àëüíûé ðàçìåð
// Ïðîâåðÿåì, íàõîäèìñÿ ëè ìû íà ðó÷êå èçìåíåíèÿ ðàçìåðà
_isResizing = IsOnRightResizeHandle(_selectedNode, _initialTouchPosition) ||
IsOnLeftResizeHandle(_selectedNode, _initialTouchPosition) ||
IsOnTopResizeHandle(_selectedNode, _initialTouchPosition) ||
IsOnBottomResizeHandle(_selectedNode, _initialTouchPosition);
}
else if (e.StatusType == GestureStatus.Running)
{
// Âû÷èñëÿåì ñìåùåíèå îò íà÷àëüíîé ïîçèöèè êàñàíèÿ
var deltaX = (float)e.TotalX - _initialTouchPosition.X;
var deltaY = (float)e.TotalY - _initialTouchPosition.Y;
if (_isResizing)
{
ResizeNode(_selectedNode, deltaX, deltaY);
}
else
{
// Ïåðåìåùåíèå
_selectedNode.Position = new PointF(
_initialNodePosition.X + deltaX,
_initialNodePosition.Y + deltaY
);
}
canvasView.Invalidate();
}
else if (e.StatusType == GestureStatus.Completed)
{
_isResizing = false;
}
}
// Èçìåíåíèå ðàçìåðà óçëà
private void ResizeNode(GraphNode node, double deltaX, double deltaY)
{
const float minSize = 50f; // Óáèðàåì "f" äëÿ double
// Óâåëè÷èâàåì èëè óìåíüøàåì ðàçìåð â çàâèñèìîñòè îò íàïðàâëåíèÿ
if (_isResizing)
{
// Åñëè ìû òÿíåì ïðàâóþ "ðó÷êó", óâåëè÷èâàåì øèðèíó
if (IsOnRightResizeHandle(node, _initialTouchPosition))
{
_selectedNode.Size = new Size(
Math.Max(minSize, node.Size.Width + deltaX), // double
_selectedNode.Size.Height // double
);
}
// Åñëè ìû òÿíåì ëåâóþ "ðó÷êó", óìåíüøàåì øèðèíó
else if (IsOnLeftResizeHandle(node, _initialTouchPosition))
{
_selectedNode.Size = new Size(
Math.Max(minSize, _selectedNode.Size.Width - deltaX), // double
_selectedNode.Size.Height // double
);
// Ïåðåìåùàåì íîäó âëåâî, ÷òîáû ñîõðàíèòü åå ïîçèöèþ
_selectedNode.Position = new Point(_selectedNode.Position.X + deltaX, _selectedNode.Position.Y); // double
}
// Åñëè ìû òÿíåì âåðõíþþ "ðó÷êó", óìåíüøàåì âûñîòó
else if (IsOnTopResizeHandle(node, _initialTouchPosition))
{
_selectedNode.Size = new Size(
_selectedNode.Size.Width, // double
Math.Max(minSize, _selectedNode.Size.Height - deltaY) // double
);
// Ïåðåìåùàåì íîäó ââåðõ, ÷òîáû ñîõðàíèòü åå ïîçèöèþ
_selectedNode.Position = new Point(_selectedNode.Position.X, _selectedNode.Position.Y + deltaY); // double
}
// Åñëè ìû òÿíåì íèæíþþ "ðó÷êó", óâåëè÷èâàåì âûñîòó
else if (IsOnBottomResizeHandle(node, _initialTouchPosition))
{
_selectedNode.Size = new Size(
_selectedNode.Size.Width, // double
Math.Max(minSize, _selectedNode.Size.Height + deltaY) // double
);
}
}
}
private bool IsOnRightResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 400;
return Math.Abs(point.X - (node.Position.X + node.Size.Width)) <= handleSize;
}
private bool IsOnLeftResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 400;
return Math.Abs(point.X - node.Position.X) <= handleSize;
}
private bool IsOnTopResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 400;
return Math.Abs(point.Y - node.Position.Y) <= handleSize;
}
private bool IsOnBottomResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 400;
return Math.Abs(point.Y - (node.Position.Y + node.Size.Height)) <= handleSize;
}
// Ìåòîä äëÿ ïðîâåðêè, íàõîäèòñÿ ëè òî÷êà âíóòðè óçëà
private bool IsPointInsideNode(PointF point, GraphNode node)
{
return point.X >= node.Position.X &&
point.X <= node.Position.X + node.Size.Width &&
point.Y >= node.Position.Y &&
point.Y <= node.Position.Y + node.Size.Height;
}
// Ïðîâåðêà, íàõîäèòñÿ ëè òî÷êà íà "ðó÷êå" èçìåíåíèÿ ðàçìåðà
private bool IsOnResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 200f; // Ðàçìåð ðó÷êè èçìåíåíèÿ ðàçìåðà
// Îïðåäåëÿåì ïðÿìîóãîëüíèêè äëÿ êàæäîé ðó÷êè
var rightHandleRect = new RectF(
(float)(node.Position.X + node.Size.Width - handleSize),
(float)(node.Position.Y + node.Size.Height - handleSize),
handleSize, handleSize);
var leftHandleRect = new RectF(
(float)(node.Position.X),
(float)(node.Position.Y + node.Size.Height - handleSize),
handleSize, handleSize);
var topHandleRect = new RectF(
(float)(node.Position.X + (node.Size.Width / 2) - (handleSize / 2)),
(float)(node.Position.Y),
handleSize, handleSize);
var bottomHandleRect = new RectF(
(float)(node.Position.X + (node.Size.Width / 2) - (handleSize / 2)),
(float)(node.Position.Y + node.Size.Height - handleSize),
handleSize, handleSize);
// Ïðîâåðÿåì, íàõîäèòñÿ ëè êóðñîð íà îäíîé èç ðó÷åê
return rightHandleRect.Contains(point) ||
leftHandleRect.Contains(point) ||
topHandleRect.Contains(point) ||
bottomHandleRect.Contains(point);
}
}
}

22
MauiProgram.cs Normal file
View file

@ -0,0 +1,22 @@
using Microsoft.Extensions.Logging;
using CommunityToolkit.Maui;
namespace NotesDataAnalyst
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit() // Подключение CommunityToolkit.Maui
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
return builder.Build();
}
}
}

View file

@ -0,0 +1,48 @@
using System.Collections.ObjectModel;
using Microsoft.Maui.Storage;
public static class CategoryManager
{
public static ObservableCollection<string> Categories { get; private set; } = new ObservableCollection<string>();
private const string ActiveCategoryKey = "ActiveCategory";
public static string ActiveCategory
{
get => Preferences.Get(ActiveCategoryKey, Categories.FirstOrDefault() ?? ""); // Проверка на null
set
{
if (!string.IsNullOrEmpty(value))
{
Preferences.Set(ActiveCategoryKey, value);
}
}
}
public static void LoadCategories()
{
if (Categories.Count == 0) // Проверка, чтобы не загружать повторно
{
Categories.Add("Личное");
Categories.Add("Работа");
Categories.Add("Дом");
// Устанавливаем первую категорию как активную, если она не задана
if (string.IsNullOrEmpty(ActiveCategory))
{
ActiveCategory = Categories.First();
}
}
}
public static void AddCategory(string category)
{
if (!Categories.Contains(category))
{
Categories.Add(category);
ActiveCategory = category; // Сразу делаем новую категорию активной
}
}
}

View file

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using System.Collections.ObjectModel;
namespace NotesDataAnalyst.NoteContent
{
public class DatabaseContext
{
private readonly string _connectionString;
public DatabaseContext(string dbPath)
{
_connectionString = $"Data Source={dbPath}";
InitializeDatabase();
}
private void InitializeDatabase()
{
using (var connection = new SqliteConnection(_connectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText =
@"
CREATE TABLE IF NOT EXISTS Notes (
Id TEXT PRIMARY KEY,
Category TEXT,
Title TEXT,
Image TEXT,
Text TEXT,
DateCreated TEXT
);
";
command.ExecuteNonQuery();
}
}
public ObservableCollection<Note> GetAllNotes()
{
var notes = new ObservableCollection<Note>();
using (var connection = new SqliteConnection(_connectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM Notes";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var note = new Note
{
Id = reader.GetString(0),
Category = reader.GetString(1),
Title = reader.GetString(2),
Image = reader.GetString(3),
Text = reader.GetString(4), // Загружаем текст из БД
DateCreated = DateTime.TryParse(reader.GetString(5), out var date) ? date : DateTime.UtcNow
};
notes.Add(note);
}
}
}
return notes;
}
public void SaveNote(Note note)
{
using (var connection = new SqliteConnection(_connectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText =
@"
INSERT OR REPLACE INTO Notes (Id, Category, Title, Image, Text, DateCreated)
VALUES ($id, $category, $title, $image, $text, $dateCreated)
";
command.Parameters.AddWithValue("$id", note.Id);
command.Parameters.AddWithValue("$category", note.Category);
command.Parameters.AddWithValue("$title", note.Title);
command.Parameters.AddWithValue("$image", note.Image);
command.Parameters.AddWithValue("$text", note.Text);
command.Parameters.AddWithValue("$dateCreated", note.DateCreated.ToString("o")); // Сохраняем в формате ISO
command.ExecuteNonQuery();
}
}
public void DeleteNote(Note note)
{
if (string.IsNullOrEmpty(note.Id))
{
Console.WriteLine("Ошибка: У заметки отсутствует Id.");
return;
}
using (var connection = new SqliteConnection(_connectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText =
@"
DELETE FROM Notes
WHERE Id = $id
";
command.Parameters.AddWithValue("$id", note.Id);
command.ExecuteNonQuery();
int result = command.ExecuteNonQuery();
Console.WriteLine(result > 0 ? "Заметка удалена из базы данных." : "Ошибка: Заметка не найдена в базе данных.");
}
}
}
}

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NotesDataAnalyst.EditorPage"
Title="">
<!-- Верхняя панель инструментов -->
<ContentPage.ToolbarItems>
<ToolbarItem Text="Сохранить" IconImageSource="save_icon.png" Priority="0" Order="Primary" Clicked="SaveButton_Clicked"/>
</ContentPage.ToolbarItems>
<!-- Основное содержимое страницы -->
<ContentPage.Content>
<StackLayout Padding="10" Spacing="1">
<!-- Категория -->
<Label Text="Category" FontSize="18" TextColor="Gray"/>
<Picker x:Name="categoryPicker"
Title="Выберите категорию"
ItemsSource="{Binding Categories}" />
<Label Text="Заголовок" FontSize="18" TextColor="Gray"/>
<Entry x:Name="titleEditor" Placeholder="Введите заголовок" FontSize="16" />
<!-- Загрузка изображения -->
<!-- Дефолтное изображение -->
<Image x:Name="imageButton"
Source="dotnet_bot.png"
HeightRequest="100"
WidthRequest="100"
Aspect="AspectFill">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnImageTapped" />
</Image.GestureRecognizers>
</Image>
<Editor x:Name="textEditor" Placeholder="Введите текст заметки" FontSize="16" HeightRequest="400" />
<!-- Нижняя панель с кнопками Markdown -->
<Grid BackgroundColor="Transparent" Padding="5" VerticalOptions="End" HorizontalOptions="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Кнопки для редактирования Markdown -->
<ImageButton Source="delete.png" Grid.Column="0" Command="{Binding InsertHeaderCommand}" CommandParameter="**Bold Text**"/>
<ImageButton Source="delete.png" Grid.Column="1" Command="{Binding ShowLinkInputCommand}" CommandParameter="_Italic Text_"/>
<ImageButton Source="pencil.png" Grid.Column="2" Command="{Binding AddLinkCommand}"/>
<ImageButton Source="delete.png" Grid.Column="3" Command="{Binding InsertMarkdownCommand}" CommandParameter="# Header"/>
<ImageButton Source="delete.png" Grid.Column="4" Command="{Binding ShowEmojiPopupCommand}" CommandParameter="- List Item"/>
</Grid>
</StackLayout>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,301 @@
using CommunityToolkit.Maui.Views;
using NotesDataAnalyst.NoteContent;
using System.Text.RegularExpressions;
using System.Windows.Input;
namespace NotesDataAnalyst
{
public partial class EditorPage : ContentPage
{
//Âûçûâàåì ýìîäçè
public ICommand ShowEmojiPopupCommand { get; }
public ICommand ShowLinkInputCommand { get; }
public ICommand InsertHeaderCommand { get; }
public ICommand AddLinkCommand { get; }
private int listItemCounter = 1; // Ñ÷¸ò÷èê äëÿ íóìåðàöèè â ñïèñêå
private readonly Note _note;
private readonly FileSaver _fileSaver;
private readonly string _filePath;
public EditorPage(Note note = null)
{
InitializeComponent();
// Èíèöèàëèçàöèÿ êîìàíä
// Èíèöèàëèçàöèÿ êîìàíä
InsertHeaderCommand = new Command(InsertHeaderTag);
// InsertBulletCommand = new Command(InsertBulletPoint);
AddLinkCommand = new Command(AddLink);
// InsertNumberedListCommand = new Command(InsertNumberedListItem);
ShowLinkInputCommand = new Command(ShowLinkInputPopup);
//Èíèöèëèçèðóåì ýìîäçè
ShowEmojiPopupCommand = new Command(ShowEmojiSelectionPopup);
BindingContext = this; // Óñòàíàâëèâàåì êîíòåêñò ïðèâÿçêè íà òåêóùèé îáúåêò ñòðàíèöû
_note = note ?? new Note(); // Åñëè note íå ïåðåäàí, ñîçäà¸ì íîâóþ çàìåòêó
_filePath = string.Empty; // Ïóñòîé ïóòü óêàçûâàåò íà ñîçäàíèå íîâîé çàìåòêè
// Çàïîëíÿåì äàííûå, åñëè ýòî ñóùåñòâóþùàÿ çàìåòêà
if (_note != null)
{
categoryPicker.SelectedItem = _note.Category;
titleEditor.Text = _note.Title;
textEditor.Text = _note.Text;
if (!string.IsNullOrEmpty(_note.Image))
{
imageButton.Source = ImageSource.FromFile(_note.Image);
}
}
// Íàñòðàèâàåì âûáîð êàòåãîðèè è óñòàíîâêó ôîêóñà
CategoryManager.LoadCategories();
categoryPicker.ItemsSource = CategoryManager.Categories;
categoryPicker.SelectedItem = CategoryManager.ActiveCategory;
string basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Notes");
_fileSaver = new FileSaver(basePath);
}
// Ðàáîòàåì ñ ýìîäçè
private async void ShowEmojiSelectionPopup()
{
var emojiPopup = new EmojiSelectionPopup();
// Îáðàáîò÷èê âûáîðà ñìàéëèêà
emojiPopup.EmojiSelected += emoji =>
{
InsertEmoji(emoji);
// Èñïîëüçóåì Dispatcher äëÿ áåçîïàñíîãî âûçîâà Close
Application.Current.Dispatcher.Dispatch(() =>
{
if (emojiPopup.Handler != null) // Ïðîâåðêà, ÷òî Popup åùå íå çàêðûò
{
emojiPopup.Close();
}
});
};
// Îòîáðàæàåì Popup
await Application.Current.MainPage.ShowPopupAsync(emojiPopup);
}
private void InsertEmoji(string emoji)
{
int cursorPosition = textEditor.CursorPosition;
textEditor.Text = textEditor.Text.Insert(cursorPosition, emoji);
textEditor.CursorPosition = cursorPosition + emoji.Length;
}
///////////////////////
///
/// <summary>
/// //////////////////////////////////////////////////////////////
/// </summary>
/// <param name="markdown"></param>
// Òýãè ìàðêäàóí
private void InsertHeaderTag()
{
if (textEditor == null)
{
Console.WriteLine("textEditor íå èíèöèàëèçèðîâàí.");
return;
}
// Ïðîâåðêà, åñëè òåêñò ïóñòîé
if (string.IsNullOrEmpty(textEditor.Text))
{
textEditor.Text = "# ";
textEditor.CursorPosition = textEditor.Text.Length;
return;
}
int cursorPosition = textEditor.CursorPosition;
string newText = cursorPosition == 0 || textEditor.Text[cursorPosition - 1] == '\n' ? "# " : "\n# ";
textEditor.Text = textEditor.Text.Insert(cursorPosition, newText);
textEditor.CursorPosition = cursorPosition + newText.Length;
}
private async void ShowLinkInputPopup()
{
var linkInputPopup = new LinkInputPopup();
// Ïîäïèñûâàåìñÿ íà ñîáûòèå, ÷òîáû ïîëó÷èòü ññûëêó è âñòàâèòü å¸ â òåêñò
linkInputPopup.LinkSubmitted += url =>
{
InsertLinkMarkdown(url);
};
// Îòêðûâàåì ìîäàëüíî
await Application.Current.MainPage.Navigation.PushModalAsync(linkInputPopup);
}
private void InsertLinkMarkdown(string url)
{
string markdownLink = $"![]({url})";
int cursorPosition = textEditor.CursorPosition;
textEditor.Text = textEditor.Text.Insert(cursorPosition, markdownLink);
textEditor.CursorPosition = cursorPosition + markdownLink.Length;
}
/* private void InsertBulletPoint()
{
if (textEditor == null)
{
Console.WriteLine("textEditor íå èíèöèàëèçèðîâàí.");
return;
}
if (string.IsNullOrEmpty(textEditor.Text))
{
textEditor.Text = "* ";
textEditor.CursorPosition = textEditor.Text.Length;
return;
}
int cursorPosition = textEditor.CursorPosition;
string bulletText = "* ";
textEditor.Text = textEditor.Text.Insert(cursorPosition, bulletText);
textEditor.CursorPosition = cursorPosition + bulletText.Length;
textEditor.Completed += (s, e) =>
{
if (!HasTwoEmptyLines(cursorPosition))
{
cursorPosition = textEditor.CursorPosition;
textEditor.Text = textEditor.Text.Insert(cursorPosition, bulletText);
textEditor.CursorPosition = cursorPosition + bulletText.Length;
}
};
}
private void InsertNumberedListItem()
{
int cursorPosition = textEditor.CursorPosition;
string listItemText = $"{listItemCounter}. ";
textEditor.Text = textEditor.Text.Insert(cursorPosition, listItemText);
textEditor.CursorPosition = cursorPosition + listItemText.Length;
// Îáíîâëÿåì íîìåð ñïèñêà ïðè ïåðåõîäå íà íîâóþ ñòðîêó
textEditor.Completed += (s, e) =>
{
if (!HasTwoEmptyLines(cursorPosition))
{
cursorPosition = textEditor.CursorPosition;
listItemCounter++;
listItemText = $"{listItemCounter}. ";
textEditor.Text = textEditor.Text.Insert(cursorPosition, listItemText);
textEditor.CursorPosition = cursorPosition + listItemText.Length;
}
else
{
listItemCounter = 1; // Ñáðîñ ñ÷¸ò÷èêà ïðè äâóõ ïóñòûõ ñòðîêàõ
}
};
}
// Ïðîâåðêà íà äâå ïóñòûå ñòðîêè ïîñëå ïîçèöèè êóðñîðà
private bool HasTwoEmptyLines(int position)
{
string textAfterCursor = textEditor.Text.Substring(position);
return Regex.IsMatch(textAfterCursor, @"\n\s*\n\s*\n");
}*/
////////////////////////////////////////////////////////////////////////////////////////////
private async void AddLink()
{
var popup = new NoteSelectionPopup();
popup.NoteSelected += OnNoteSelectedForLink;
await this.ShowPopupAsync(popup);
}
private void OnNoteSelectedForLink(Note selectedNote)
{
textEditor.Text = NoteLinkManager.InsertLink(textEditor.Text, selectedNote);
}
private async void OnImageTapped(object sender, EventArgs e)
{
try
{
var result = await FilePicker.PickAsync(new PickOptions
{
FileTypes = FilePickerFileType.Images,
PickerTitle = "Âûáåðèòå èçîáðàæåíèå"
});
if (result != null)
{
_note.Image = result.FullPath;
imageButton.Source = ImageSource.FromFile(result.FullPath);
}
}
catch (Exception ex)
{
await DisplayAlert("Îøèáêà", $"Íå óäàëîñü çàãðóçèòü èçîáðàæåíèå: {ex.Message}", "OK");
}
}
private async void SaveButton_Clicked(object sender, EventArgs e)
{
// Åñëè ó çàìåòêè íåò Id, ïðèñâàèâàåì íîâûé Id
if (string.IsNullOrEmpty(_note.Id))
{
_note.Id = Guid.NewGuid().ToString();
}
var note = new Note
{ Id = _note.Id,
Category = categoryPicker.SelectedItem as string,
Title = titleEditor.Text,
Image = _note.Image,
Text = textEditor.Text,
FilePath = _filePath
};
// Ïðîâåðêà çàãîëîâêà
if (string.IsNullOrEmpty(note.Title))
{
// Ïîëó÷àåì ïåðâûå 4 ñëîâà èç òåêñòà
var words = note.Text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
note.Title = string.Join(" ", words.Take(4));
}
//Çàïèñü â ÁÄ ïîêà îòêëþ÷åíà
await Task.Run(() => App.Database.SaveNote(note));
// Îòïðàâëÿåì ñîîáùåíèå äëÿ îáíîâëåíèÿ ñïèñêà çàìåòîê íà ãëàâíîé ñòðàíèöå
MessagingCenter.Send(this, "NoteSaved", _note);
await DisplayAlert("Success", "Note saved successfully.", "OK");
await Navigation.PushAsync(new MainPage());
}
}
}

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="NotesDataAnalyst.EmojiSelectionPopup">
<!-- Внутри Frame мы можем установить цвет фона -->
<!-- Задаем фон внутри Frame -->
<Frame BackgroundColor="White"
CornerRadius="10"
Padding="10"
VerticalOptions="Center"
HorizontalOptions="Center"
WidthRequest="300">
<StackLayout>
<Label Text="Выберите смайлик" FontSize="18" HorizontalOptions="Center" />
<!-- Прокручиваемая область для смайликов -->
<ScrollView HeightRequest="200">
<CollectionView x:Name="emojiCollectionView"
ItemsSource="{Binding EmojiList}"
SelectionMode="Single"
SelectionChanged="OnEmojiSelected">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="4" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding}"
FontSize="24"
HorizontalOptions="Center"
VerticalOptions="Center"
Padding="10" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ScrollView>
<!-- Кнопка для закрытия окна -->
<Button Text="Закрыть" Clicked="OnCloseButtonClicked" HorizontalOptions="Center" />
</StackLayout>
</Frame>
</toolkit:Popup>

View file

@ -0,0 +1,44 @@
using CommunityToolkit.Maui.Views;
using System.Collections.ObjectModel;
namespace NotesDataAnalyst;
public partial class EmojiSelectionPopup : Popup
{
public event Action<string>? EmojiSelected;
public ObservableCollection<string> EmojiList { get; set; }
public EmojiSelectionPopup()
{
InitializeComponent();
EmojiList = new ObservableCollection<string>
{
"🌍", "😊", "🎉", "👍","📑","📝","✏️","📆","💼","👜","💄","⌚","💎","⛏️",
"❤️", "🔥", "✨", "🚀",
// Добавьте больше смайликов по мере необходимости
};
BindingContext = this;
}
// Обработчик выбора смайлика
private void OnEmojiSelected(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is string selectedEmoji)
{
EmojiSelected?.Invoke(selectedEmoji);
// Close(); // Используем Close для закрытия окна
}
}
private void OnCloseButtonClicked(object sender, EventArgs e)
{
Close(); // Используем Close при нажатии на кнопку
}
}

70
NoteContent/FileLoader.cs Normal file
View file

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
/*namespace NotesDataAnalyst
{
public static class FileLoader
{
// Загрузка одной заметки из указанного файла
public static async Task<Note?> LoadDataAsync(string filePath)
{
if (!File.Exists(filePath))
{
return null; // Если файл не существует, возвращаем null
}
try
{
// Чтение JSON-содержимого из файла
string jsonString = await File.ReadAllTextAsync(filePath);
// Десериализация в объект Note
Note? note = JsonSerializer.Deserialize<Note>(jsonString);
if (note != null)
{
note.FilePath = filePath; // Устанавливаем путь к файлу, если загрузка успешна
}
return note;
}
catch (JsonException ex)
{
Console.WriteLine($"Ошибка десериализации JSON: {ex.Message}");
return null;
}
}
// Загрузка всех заметок из базовой папки
/* public static async Task<List<Note>> LoadAllNotesAsync(string basePath)
{
var notes = new List<Note>();
// Проверяем, существует ли базовая папка
if (!Directory.Exists(basePath))
{
return notes; // Если папки нет, возвращаем пустой список
}
// Проходим по каждой папке категории
foreach (string categoryFolder in Directory.GetDirectories(basePath))
{
// Проходим по всем JSON-файлам в папке категории
foreach (string filePath in Directory.GetFiles(categoryFolder, "*.json"))
{
Note? note = await LoadDataAsync(filePath); // Загружаем заметку из файла
if (note != null)
{
notes.Add(note); // Добавляем в список, если успешно загружено
}
}
}
return notes; // Возвращаем список всех загруженных заметок
}
}
}*/

83
NoteContent/FileSaver.cs Normal file
View file

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace NotesDataAnalyst.NoteContent
{
public class FileSaver : INotifyPropertyChanged
{
// Базовый путь, где будут храниться все заметки
private readonly string _basePath;
private ObservableCollection<string> _files;
// Коллекция для хранения путей к сохранённым файлам
public ObservableCollection<string> Files
{
get => _files;
private set
{
_files = value;
OnPropertyChanged(nameof(Files));
}
}
public FileSaver(string basePath)
{
// Устанавливаем базовый путь и инициализируем коллекцию файлов
_basePath = basePath;
Files = new ObservableCollection<string>();
}
// Создаёт путь к файлу на основе категории и заголовка
private string GenerateFilePath(Note note)
{
// Определяем путь для папки категории
string categoryFolder = Path.Combine(_basePath, note.Category);
// Создаём папку, если её ещё нет
Directory.CreateDirectory(categoryFolder);
// Преобразуем заголовок в имя файла, удаляя недопустимые символы
string fileName = SanitizeFileName(note.Title) + ".json";
// Полный путь к файлу
return Path.Combine(categoryFolder, fileName);
}
// Удаляет недопустимые символы из заголовка для имени файла
private string SanitizeFileName(string title)
{
// Заменяем символы, которые нельзя использовать в именах файлов, на подчёркивания
return Regex.Replace(title, @"[<>:""/\|?*]", "_");
}
public void SaveData(Note note)
{
// Если у заметки есть путь к файлу, обновляем существующий файл
string filePath = !string.IsNullOrEmpty(note.FilePath)
? note.FilePath
: GenerateFilePath(note);
string jsonString = JsonSerializer.Serialize(note);
File.WriteAllText(filePath, jsonString);
// Если это новая заметка, добавляем её путь в коллекцию
if (!Files.Contains(filePath))
{
Files.Add(filePath);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NotesDataAnalyst.LinkInputPopup"
BackgroundColor="Transparent"
Padding="20">
<Frame BackgroundColor="White"
CornerRadius="10"
Padding="20"
VerticalOptions="Center"
HorizontalOptions="Center">
<StackLayout>
<Label Text="Введите URL ссылки" FontSize="18" HorizontalOptions="Center"/>
<Entry x:Name="urlEntry" Placeholder="https://example.com" Keyboard="Url"/>
<Button Text="Добавить" Clicked="OnAddButtonClicked"/>
<Button Text="Отмена" Clicked="OnCancelButtonClicked"/>
</StackLayout>
</Frame>
</ContentPage>

View file

@ -0,0 +1,33 @@
namespace NotesDataAnalyst;
public partial class LinkInputPopup : ContentPage
{
public event Action<string>? LinkSubmitted;
public LinkInputPopup()
{
InitializeComponent();
}
private void OnAddButtonClicked(object sender, EventArgs e)
{
string url = urlEntry.Text;
if (!string.IsNullOrEmpty(url))
{
LinkSubmitted?.Invoke(url); // Ïåðåäà÷à ââåäåííîé ññûëêè îáðàòíî
ClosePopup();
}
}
private void OnCancelButtonClicked(object sender, EventArgs e)
{
ClosePopup();
}
private void ClosePopup()
{
// Çàêðûòèå îêíà (èìèòèðóåì ïîâåäåíèå Popup)
Application.Current.MainPage.Navigation.PopModalAsync();
}
}

86
NoteContent/MainPage.xaml Normal file
View file

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NotesDataAnalyst.MainPage">
<!-- Панель инструментов для добавления новой заметки -->
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" IconImageSource="add_icon.png" Priority="0" Order="Primary" Clicked="AddNoteButton_Clicked"/>
</ContentPage.ToolbarItems>
<!-- Контейнер контента страницы -->
<ContentPage.Content>
<StackLayout Padding="20" Spacing="15">
<!-- Панель поиска -->
<SearchBar x:Name="searchBar"
Placeholder="Поиск по заметкам"
TextChanged="OnSearchTextChanged" />
<!-- Выпадающий список категорий -->
<Picker x:Name="categoryPicker"
Title="Выберите категорию"
ItemsSource="{Binding Categories}"
SelectedIndexChanged="OnCategorySelected" />
<!-- Коллекция заметок с возможностью прокрутки и анимацией -->
<CollectionView x:Name="notesCollectionView"
ItemsSource="{Binding Notes}"
VerticalScrollBarVisibility="Always"
HeightRequest="500">
<!-- Устанавливаем высоту CollectionView -->
<!-- Настройка вертикального расположения с отступами -->
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="15"/>
</CollectionView.ItemsLayout>
<!-- Шаблон отображения элемента -->
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame Padding="0" Margin="5" CornerRadius="8" BackgroundColor="Transparent">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnNoteTapped" />
</Frame.GestureRecognizers>
<!-- Содержимое элемента (изображение и заголовок) -->
<StackLayout Orientation="Vertical"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
WidthRequest="300"
HeightRequest="150">
<!-- Изображение заметки -->
<Image Source="{Binding Image}"
Aspect="AspectFill"
HeightRequest="120"
WidthRequest="300"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image.Triggers>
<DataTrigger TargetType="Image" Binding="{Binding Image}" Value="{x:Null}">
<Setter Property="Source" Value="placeholder_image.png" />
</DataTrigger>
</Image.Triggers>
</Image>
<!-- Заголовок заметки -->
<Label Text="{Binding Title}"
FontSize="18"
FontAttributes="Bold"
HorizontalOptions="Center"
VerticalOptions="End"
TextColor="Black" />
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,247 @@
using System.Collections.ObjectModel;
using NotesDataAnalyst.NoteContent;
namespace NotesDataAnalyst
{
// MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public ObservableCollection<Note> Notes { get; set; }
public ObservableCollection<Note> FilteredNotes { get; set; }
public MainPage()
{
InitializeComponent();
BindingContext = this;
// Подписываемся на сообщения для обновления списка заметок
MessagingCenter.Subscribe<EditorPage, Note>(this, "NoteSaved", (sender, savedNote) =>
{
var existingNote = Notes.FirstOrDefault(n => n.Id == savedNote.Id);
if (existingNote != null)
{
var index = Notes.IndexOf(existingNote);
Notes.Remove(existingNote);
Notes.Insert(0, savedNote);
}
else
{
// Вставляем новую заметку в начало списка
Notes.Insert(0, savedNote);
}
ApplyCategoryFilter();
});
MessagingCenter.Subscribe<NoteDetailPage, Note>(this, "NoteDeleted", (sender, deletedNote) =>
{
Notes.Remove(deletedNote);
ApplyCategoryFilter();
});
// Загружаем заметки из базы данных
Notes = App.Database.GetAllNotes();
// Инициализация и загрузка данных
CategoryManager.LoadCategories();
categoryPicker.ItemsSource = CategoryManager.Categories;
categoryPicker.SelectedItem = CategoryManager.ActiveCategory ?? CategoryManager.Categories.FirstOrDefault();
// Notes = new ObservableCollection<Note>();
FilteredNotes = new ObservableCollection<Note>();
BindingContext = this;
}
protected override async void OnAppearing()
{
base.OnAppearing();
// Загружаем отсортированные заметки
Notes = new ObservableCollection<Note>(App.Database.GetAllNotes().OrderByDescending(n => n.DateCreated));
// Применяем фильтрацию
ApplyCategoryFilter();
// Обновляем выбранную категорию в Picker
categoryPicker.SelectedItem = CategoryManager.ActiveCategory;
}
/* private void OnCollectionViewScrolled(object sender, ItemsViewScrolledEventArgs e)
{
// Получаем центр экрана по оси Y
double centerScreenY = notesCollectionView.Height / 2;
// Высота элемента (подберите в зависимости от реальной высоты элементов в CollectionView)
double itemHeight = 150;
// Итерируем видимые элементы по их индексам
for (int i = e.FirstVisibleItemIndex; i <= e.LastVisibleItemIndex; i++)
{
// Находим элемент с индексом i
var note = Notes[i]; // предполагаем, что Notes - ObservableCollection<Note>
// Получаем фрейм элемента через шаблон
var item = notesCollectionView.ItemTemplate.CreateContent() as Frame;
if (item == null) continue;
// Устанавливаем BindingContext для анимации текущего элемента
item.BindingContext = note;
// Позиция центра элемента
double itemCenterY = (i * itemHeight) + (itemHeight / 2);
// Расчёт расстояния до центра экрана
double distanceFromCenter = Math.Abs(centerScreenY - itemCenterY);
// Настройка масштаба и прозрачности
double scale = Math.Max(0.75, 1 - (distanceFromCenter / centerScreenY));
double opacity = Math.Max(0.5, 1 - (distanceFromCenter / centerScreenY));
// Анимации: масштабирование и изменение прозрачности
item.ScaleTo(scale, 250, Easing.CubicInOut);
item.FadeTo(opacity, 250, Easing.CubicInOut);
}
}*/
private async void OnNoteTapped(object sender, EventArgs e)
{
// Получаем контекст заметки, на которую был выполнен клик
var frame = sender as Frame;
var selectedNote = frame?.BindingContext as Note;
if (selectedNote != null)
{
// Переход на страницу NoteDetailPage с выбранной заметкой
await Navigation.PushAsync(new NoteDetailPage(selectedNote));
}
}
// Обработчик для изменения текста в панели поиска
private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
{
ApplySearchFilter(e.NewTextValue);
}
private void ApplySearchFilter(string searchText)
{
FilteredNotes.Clear();
var filteredNotes = Notes
.Where(note => string.IsNullOrEmpty(searchText) ||
(note.Title?.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0))
.ToList();
foreach (var note in filteredNotes)
{
FilteredNotes.Add(note);
}
}
private ObservableCollection<Note> LoadNotes()
{
// Здесь загружаем заметки из базы данных или локального хранилища
return new ObservableCollection<Note>();
}
// Обработчик для изменения выбранной категории
private void OnCategorySelected(object sender, EventArgs e)
{
// Проверяем, что categoryPicker.SelectedItem не является null
if (categoryPicker.SelectedItem == null)
return;
string selectedCategory = categoryPicker.SelectedItem as string;
// Устанавливаем активную категорию
CategoryManager.ActiveCategory = selectedCategory;
// Проверяем, что Notes и selectedCategory не null перед фильтрацией
if (Notes == null || selectedCategory == null)
{
FilteredNotes = new ObservableCollection<Note>(Notes ?? new ObservableCollection<Note>());
}
else if (selectedCategory == "Все категории")
{
FilteredNotes = new ObservableCollection<Note>(Notes);
}
else
{
FilteredNotes = new ObservableCollection<Note>(Notes.Where(note => note.Category == selectedCategory));
}
// Обновляем привязку данных CollectionView
notesCollectionView.ItemsSource = FilteredNotes;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
// Отписываемся от сообщения при закрытии страницы
MessagingCenter.Unsubscribe<NoteDetailPage, Note>(this, "NoteDeleted");
}
private async void OnNoteSelected(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is Note selectedNote)
{
// Переход на NoteDetailPage вместо EditorPage
await Navigation.PushAsync(new NoteDetailPage(selectedNote));
}
((CollectionView)sender).SelectedItem = null;
}
/* protected override async void OnAppearing()
{
base.OnAppearing();
// Загружаем заметки асинхронно
// Notes = await LoadNotesAsync();
// Применяем фильтрацию на основе `ActiveCategory`
ApplyCategoryFilter();
// Обновляем `Picker` с категориями
categoryPicker.SelectedItem = CategoryManager.ActiveCategory;
}*/
private void ApplyCategoryFilter()
{
if (CategoryManager.ActiveCategory == "Все категории" || string.IsNullOrEmpty(CategoryManager.ActiveCategory))
{
FilteredNotes = new ObservableCollection<Note>(Notes);
}
else
{
// Фильтрация по категории
FilteredNotes = new ObservableCollection<Note>(Notes.Where(note => note.Category == CategoryManager.ActiveCategory));
}
// Обновляем привязку данных для CollectionView
notesCollectionView.ItemsSource = FilteredNotes;
}
private async void AddNoteButton_Clicked(object sender, EventArgs e)
{
// Создаём новую пустую заметку
var newNote = new Note();
// Переход на EditorPage с новой заметкой
await Navigation.PushAsync(new EditorPage());
}
}
}

65
NoteContent/Note.cs Normal file
View file

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Markdig;
namespace NotesDataAnalyst.NoteContent
{
public static class MarkdownExtensions
{
public static string ToHtml(string markdownText)
{
// Создание конвейера с необходимыми расширениями
var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions() // Подключение большинства популярных расширений
.UseEmojiAndSmiley() // Поддержка эмодзи и смайлов
.UseTaskLists() // Поддержка списков задач
.UseDiagrams() // Поддержка диаграмм
.Build();
return Markdown.ToHtml(markdownText, pipeline);
}
}
public class Note
{
public string Id { get; set; } = Guid.NewGuid().ToString(); // Уникальный идентификатор для заметки
// Категория заметки (например, "Личное", "Работа", и т.д.)
public string Category { get; set; }
// Заголовок заметки, используется также как часть имени файла
public string Title { get; set; }
// Поле для хранения ссылки на изображение
public string Image { get; set; }
// Основной текст заметки, который пользователь может вводить в формате Markdown
public string Text { get; set; }
// Свойство для пути к файлу заметки
public string FilePath { get; set; }
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
// Свойство для отображения текста в формате HTML
// Используем Markdig для преобразования текста из Markdown в HTML
public string FormattedText => MarkdownExtensions.ToHtml(Text);
public string FormattedTitle => MarkdownExtensions.ToHtml(Title);
}
}

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NotesDataAnalyst.NoteDetailPage"
Title="">
<ContentPage.Content>
<StackLayout Padding="10" Spacing="10">
<!-- Заголовок заметки -->
<Label x:Name="titleLabel"
FontSize="24"
FontAttributes="Bold"
TextColor="Black"
HorizontalOptions="Center" />
<!-- Изображение (если указано) -->
<Image x:Name="imageView"
HeightRequest="150"
Aspect="AspectFill"
IsVisible="False" />
<!-- Текст заметки -->
<WebView x:Name="contentWebView" HeightRequest="430" Navigating="OnWebViewNavigating">
<WebView.Source>
<HtmlWebViewSource Html="{Binding FormattedText}"/>
</WebView.Source>
</WebView>
<!-- Кнопки "Редактировать" и "Удалить" -->
<StackLayout Orientation="Horizontal" HorizontalOptions="Center" Spacing="20">
<Button Text="Редактировать"
BackgroundColor="#4CAF50"
TextColor="White"
Clicked="OnEditButtonClicked" />
<Button Text="Удалить"
BackgroundColor="#f44336"
TextColor="White"
Clicked="OnDeleteButtonClicked" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>

View file

@ -0,0 +1,135 @@
using Markdig.Syntax.Inlines;
using Markdig;
using System.Text.RegularExpressions;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using System.Collections.ObjectModel;
using NotesDataAnalyst.NoteContent;
namespace NotesDataAnalyst;
public partial class NoteDetailPage : ContentPage
{
private readonly Note _note;
public NoteDetailPage(Note note)
{
InitializeComponent();
_note = note;
// Óñòàíàâëèâàåì äàííûå çàìåòêè â ýëåìåíòû óïðàâëåíèÿ
titleLabel.Text = _note.Title;
//textLabel.Text = _note.Text;
BindingContext = _note;
if (!string.IsNullOrEmpty(_note.Image))
{
imageView.Source = _note.Image;
imageView.IsVisible = true;
}
ApplyThemeToHtml(); // Ïðèìåíÿåì òåìó ê HTML-êîíòåíòó WebView
}
/// //////////////////////////////////// Ðàáîòàåì ñ òåìîé Webwiev
private void ApplyThemeToHtml()
{
bool isDarkTheme = Application.Current.RequestedTheme == AppTheme.Dark;
string backgroundColor = isDarkTheme ? "#212121" : "#FFFFFF";
string textColor = isDarkTheme ? "#FFFFFF" : "#000000";
// Ôîðìèðóåì HTML-êîíòåíò ñ CSS-ñòèëÿìè
string htmlContent = $@"
<html>
<head>
<style>
body {{
background-color: {backgroundColor};
color: {textColor};
font-family: Arial, sans-serif;
margin: 0;
padding: 10px;
}}
</style>
</head>
<body>
{_note.FormattedText}
</body>
</html>";
contentWebView.Source = new HtmlWebViewSource { Html = htmlContent };
}
protected override void OnAppearing()
{
base.OnAppearing();
Application.Current.RequestedThemeChanged += OnRequestedThemeChanged;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
Application.Current.RequestedThemeChanged -= OnRequestedThemeChanged;
}
private void OnRequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
{
ApplyThemeToHtml(); // Ïåðåçàãðóæàåì HTML ñ íîâîé òåìîé
}
/// //////////////////////////////////////////////////////////
// Îáðàáîò÷èê íàâèãàöèè WebView äëÿ ïåðåõâàòà ññûëîê
private async void OnWebViewNavigating(object sender, WebNavigatingEventArgs e)
{
if (e.Url.StartsWith("note:"))
{
e.Cancel = true; // Îòìåíÿåì ïåðåõîä
// Èçâëåêàåì Id çàìåòêè èç ññûëêè
string noteId = e.Url.Replace("note:", "");
await NoteLinkManager.NavigateToNoteByIdAsync(noteId, Navigation);
}
}
// Îáðàáîò÷èê êíîïêè "Ðåäàêòèðîâàòü"
private async void OnEditButtonClicked(object sender, EventArgs e)
{
// Âìåñòî ïåðåäà÷è _note.FilePath ïåðåäàåì ñàì îáúåêò _note
await Navigation.PushAsync(new EditorPage(_note));
}
// Îáðàáîò÷èê êíîïêè "Óäàëèòü"
private async void OnDeleteButtonClicked(object sender, EventArgs e)
{
bool confirm = await DisplayAlert("Ïîäòâåðæäåíèå", "Âû óâåðåíû, ÷òî õîòèòå óäàëèòü ýòó çàìåòêó?", "Óäàëèòü", "Îòìåíà");
if (confirm)
{
// Óäàëÿåì ôàéë çàìåòêè
// if (File.Exists(_note.FilePath))
// { //Óäàëåíèå èç ÁÄ ïîêà íå ðàáîòàåò
// await Task.Run (() => App.DbContext.DeleteNote(_note));
// File.Delete(_note.FilePath);
// Óäàëÿåì çàìåòêó èç áàçû äàííûõ
App.Database.DeleteNote(_note);
await DisplayAlert("Îïîâåùåíèå", "Ôàéë óäàëåí", "Ok");
// Îòïðàâëÿåì ñîîáùåíèå äëÿ îáíîâëåíèÿ ñïèñêà çàìåòîê
MessagingCenter.Send(this, "NoteDeleted", _note);
// Âîçâðàùàåìñÿ íà ãëàâíóþ ñòðàíèöó è îáíîâëÿåì ñïèñîê
await Navigation.PushAsync(new MainPage());
// }
// else { await DisplayAlert("Îøèáêà", "Çàïèñü íå ìîæåò áûòü óäàëåíà, òàê êàê ôàéë íå íàéäåí", "Ok"); }
}
}
}

View file

@ -0,0 +1,48 @@
using System.Text.RegularExpressions;
using Microsoft.Maui.Controls;
namespace NotesDataAnalyst.NoteContent
{
public static class NoteLinkManager
{
private static readonly Regex LinkPattern = new(@"<a href=""note:(.*?)"">(.*?)</a>", RegexOptions.Compiled);
// Метод для вставки HTML-ссылки на заметку
public static string InsertLink(string currentText, Note selectedNote)
{
string linkText = $"<a href=\"note:{selectedNote.Id}\">{selectedNote.Title}</a>";
return currentText + " " + linkText;
}
// Метод для извлечения ссылок в HTML
public static List<(string Id, string Title)> ParseLinks(string html)
{
var links = new List<(string Id, string Title)>();
var matches = LinkPattern.Matches(html);
foreach (Match match in matches)
{
string id = match.Groups[1].Value;
string title = match.Groups[2].Value;
links.Add((id, title));
}
return links;
}
// Реализуем метод NavigateToNoteByIdAsync для перехода к заметке по Id
public static async Task NavigateToNoteByIdAsync(string noteId, INavigation navigation)
{
var note = App.Database.GetAllNotes().FirstOrDefault(n => n.Id == noteId);
if (note != null)
{
await navigation.PushAsync(new NoteDetailPage(note));
}
else
{
await Application.Current.MainPage.DisplayAlert("Ошибка", "Заметка не найдена", "OK");
}
}
}
}

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="NotesDataAnalyst.NoteSelectionPopup">
<StackLayout Padding="20" BackgroundColor="White" WidthRequest="300">
<Label Text="Выберите заметку для ссылки" FontSize="18" FontAttributes="Bold" HorizontalOptions="Center" />
<SearchBar x:Name="searchBar" Placeholder="Поиск заметок" TextChanged="OnSearchTextChanged" />
<CollectionView x:Name="notesCollectionView" SelectionMode="Single" SelectionChanged="OnNoteSelected">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame Padding="10" Margin="5" BackgroundColor="LightGray" CornerRadius="8">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnNoteTapped" />
</Frame.GestureRecognizers>
<Label Text="{Binding Title}" FontSize="16" TextColor="Black" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Отмена" Clicked="OnCancelButtonClicked" BackgroundColor="#f44336" TextColor="White"/>
</StackLayout>
</toolkit:Popup>

View file

@ -0,0 +1,59 @@
using CommunityToolkit.Maui.Views;
using NotesDataAnalyst.NoteContent;
namespace NotesDataAnalyst
{
public partial class NoteSelectionPopup : Popup
{
public event Action<Note> NoteSelected;
public NoteSelectionPopup()
{
InitializeComponent();
notesCollectionView.ItemsSource = App.Database.GetAllNotes(); // Çàãðóçêà çàìåòîê èç ÁÄ
}
private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
{
string searchText = e.NewTextValue.ToLower();
notesCollectionView.ItemsSource = App.Database.GetAllNotes()
.Where(note => note.Title.ToLower().Contains(searchText));
}
private async void OnNoteSelected(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is Note selectedNote)
{
NoteSelected?.Invoke(selectedNote);
Close(); // Çàêðûâàåì îêíî ïîñëå âûáîðà çàìåòêè
}
}
private async void OnNoteTapped(object sender, EventArgs e)
{
// Ïîëó÷àåì ýëåìåíò, íà êîòîðûé êëèêíóëè
var tappedFrame = (Frame)sender;
var selectedNote = (Note)tappedFrame.BindingContext;
if (selectedNote != null)
{
NoteSelected?.Invoke(selectedNote);
Close(); // Çàêðûâàåì îêíî ïîñëå âûáîðà çàìåòêè
}
}
private void OnCancelButtonClicked(object sender, EventArgs e)
{
Close(); // Çàêðûòèå îêíà ïî íàæàòèþ íà êíîïêó Îòìåíà
}
}
}

89
NotesDataAnalyst.csproj Normal file
View file

@ -0,0 +1,89 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
<!-- Note for MacCatalyst:
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->
<OutputType>Exe</OutputType>
<RootNamespace>NotesDataAnalyst</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Display name -->
<ApplicationTitle>NotesDataAnalyst</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.companyname.notesdataanalyst</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="9.1.0" />
<PackageReference Include="Markdig" Version="0.38.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0-rc.2.24474.1" />
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.92" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<MauiXaml Update="GraphPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NoteContent\EditorPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NoteContent\EmojiSelectionPopup.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NoteContent\LinkInputPopup.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NoteContent\NoteDetailPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NoteContent\NoteSelectionPopup.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>
</Project>

27
NotesDataAnalyst.sln Normal file
View file

@ -0,0 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotesDataAnalyst", "NotesDataAnalyst.csproj", "{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Release|Any CPU.Build.0 = Release|Any CPU
{8AAA7775-10E2-43A6-ACB4-DDD53C514A9E}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB40F5E4-7F23-47F8-9832-56F12E8279A1}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View file

@ -0,0 +1,11 @@
using Android.App;
using Android.Content.PM;
using Android.OS;
namespace NotesDataAnalyst
{
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
}
}

View file

@ -0,0 +1,16 @@
using Android.App;
using Android.Runtime;
namespace NotesDataAnalyst
{
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512BD4</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#2B0B98</color>
</resources>

View file

@ -0,0 +1,10 @@
using Foundation;
namespace NotesDataAnalyst
{
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<!-- See https://aka.ms/maui-publish-app-store#add-entitlements for more information about adding entitlements.-->
<dict>
<!-- App Sandbox must be enabled to distribute a MacCatalyst app through the Mac App Store. -->
<key>com.apple.security.app-sandbox</key>
<true/>
<!-- When App Sandbox is enabled, this value is required to open outgoing network connections. -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- The Mac App Store requires you specify if the app uses encryption. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption -->
<!-- <key>ITSAppUsesNonExemptEncryption</key> -->
<!-- Please indicate <true/> or <false/> here. -->
<!-- Specify the category for your app here. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype -->
<!-- <key>LSApplicationCategoryType</key> -->
<!-- <string>public.app-category.YOUR-CATEGORY-HERE</string> -->
<key>UIDeviceFamily</key>
<array>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>

View file

@ -0,0 +1,16 @@
using ObjCRuntime;
using UIKit;
namespace NotesDataAnalyst
{
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}

17
Platforms/Tizen/Main.cs Normal file
View file

@ -0,0 +1,17 @@
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using System;
namespace NotesDataAnalyst
{
internal class Program : MauiApplication
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
static void Main(string[] args)
{
var app = new Program();
app.Run(args);
}
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="8" xmlns="http://tizen.org/ns/packages">
<profile name="common" />
<ui-application appid="maui-application-id-placeholder" exec="NotesDataAnalyst.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
<label>maui-application-title-placeholder</label>
<icon>maui-appicon-placeholder</icon>
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
</ui-application>
<shortcut-list />
<privileges>
<privilege>http://tizen.org/privilege/internet</privilege>
</privileges>
<dependencies />
<provides-appdefined-privileges />
</manifest>

View file

@ -0,0 +1,8 @@
<maui:MauiWinUIApplication
x:Class="NotesDataAnalyst.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maui="using:Microsoft.Maui"
xmlns:local="using:NotesDataAnalyst.WinUI">
</maui:MauiWinUIApplication>

View file

@ -0,0 +1,25 @@
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace NotesDataAnalyst.WinUI
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="B0B711CC-EE95-4BD5-88F0-97C4A359FA13" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
<uap:SplashScreen Image="$placeholder$.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="NotesDataAnalyst.WinUI.app"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View file

@ -0,0 +1,10 @@
using Foundation;
namespace NotesDataAnalyst
{
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}

32
Platforms/iOS/Info.plist Normal file
View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>

16
Platforms/iOS/Program.cs Normal file
View file

@ -0,0 +1,16 @@
using ObjCRuntime;
using UIKit;
namespace NotesDataAnalyst
{
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}

View file

@ -0,0 +1,8 @@
{
"profiles": {
"Windows Machine": {
"commandName": "MsixPackage",
"nativeDebugging": false
}
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
</svg>

After

Width:  |  Height:  |  Size: 228 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Binary file not shown.

BIN
Resources/Images/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
Resources/Images/adda.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

BIN
Resources/Images/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
Resources/Images/pencil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

BIN
Resources/Images/upload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,15 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories). Deployment of the asset to your application
is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
These files will be deployed with you package and will be accessible using Essentials:
async Task LoadMauiAsset()
{
using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
}

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<!-- Note: For Android please see also Platforms\Android\Resources\values\colors.xml -->
<Color x:Key="Primary">#512BD4</Color>
<Color x:Key="PrimaryDark">#ac99ea</Color>
<Color x:Key="PrimaryDarkText">#242424</Color>
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="SecondaryDarkText">#9880e5</Color>
<Color x:Key="Tertiary">#2B0B98</Color>
<Color x:Key="White">White</Color>
<Color x:Key="Black">Black</Color>
<Color x:Key="Magenta">#D600AA</Color>
<Color x:Key="MidnightBlue">#190649</Color>
<Color x:Key="OffBlack">#1f1f1f</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray300">#ACACAC</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="Gray950">#141414</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"/>
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}"/>
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}"/>
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}"/>
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}"/>
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}"/>
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}"/>
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}"/>
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}"/>
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}"/>
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}"/>
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}"/>
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}"/>
</ResourceDictionary>

View file

@ -0,0 +1,426 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Style TargetType="ActivityIndicator">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="IndicatorView">
<Setter Property="IndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}"/>
<Setter Property="SelectedIndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}"/>
</Style>
<Style TargetType="Border">
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="StrokeShape" Value="Rectangle"/>
<Setter Property="StrokeThickness" Value="1"/>
</Style>
<Style TargetType="BoxView">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Button">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource PrimaryDarkText}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="DatePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Editor">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Entry">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Frame">
<Setter Property="HasShadow" Value="False" />
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style TargetType="ImageButton">
<Setter Property="Opacity" Value="1" />
<Setter Property="BorderColor" Value="Transparent"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Opacity" Value="0.5" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Span">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="Label" x:Key="Headline">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
<Setter Property="FontSize" Value="32" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<Style TargetType="Label" x:Key="SubHeadline">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
<Setter Property="FontSize" Value="24" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<Style TargetType="ListView">
<Setter Property="SeparatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="RefreshControlColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Picker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RadioButton">
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RefreshView">
<Setter Property="RefreshColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="SearchBar">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchHandler">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Shadow">
<Setter Property="Radius" Value="15" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Brush" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Offset" Value="10,10" />
</Style>
<Style TargetType="Slider">
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SwipeItem">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Switch">
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="ThumbColor" Value="{StaticResource White}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="TimePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Padding" Value="0"/>
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
</Style>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
<Setter Property="Shell.ForegroundColor" Value="{OnPlatform WinUI={StaticResource Primary}, Default={StaticResource White}}" />
<Setter Property="Shell.TitleColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
<Setter Property="Shell.DisabledColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.UnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
<Setter Property="Shell.NavBarHasShadow" Value="False" />
<Setter Property="Shell.TabBarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="Shell.TabBarForegroundColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarTitleColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarUnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
<Setter Property="IconColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="TabbedPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="UnselectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="SelectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
</ResourceDictionary>