Fix markdown draft preview sync in PostIt blog editor
This commit is contained in:
parent
84c98cb938
commit
7e9e7613ba
4 changed files with 209 additions and 90 deletions
51
src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml
Normal file
51
src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<UserControl
|
||||
x:Class="PostIt.Controls.MarkdownEditorControl"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:AvaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
|
||||
xmlns:md="using:MarkdownViewer.Core.Controls"
|
||||
x:Name="Root"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Border
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
IsVisible="{Binding !IsPreviewVisible, ElementName=Root}"
|
||||
Padding="0">
|
||||
<AvaloniaEdit:TextEditor
|
||||
x:Name="SourceEditor"
|
||||
Document="{Binding Document, ElementName=Root, Mode=TwoWay}"
|
||||
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"
|
||||
ShowLineNumbers="True"
|
||||
WordWrap="True"
|
||||
BorderThickness="0"
|
||||
Padding="5,5,5,15"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
TextInputOptions.Multiline="True"
|
||||
TextInputOptions.ReturnKeyType="Default" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
IsVisible="{Binding IsPreviewVisible, ElementName=Root}"
|
||||
Padding="8">
|
||||
<ScrollViewer
|
||||
BringIntoViewOnFocusChange="False"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<md:MarkdownViewer
|
||||
x:Name="PreviewViewer"
|
||||
MarkdownText="{Binding MarkdownText, ElementName=Root}"
|
||||
ClipToBounds="False" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
63
src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs
Normal file
63
src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using AvaloniaEdit.Document;
|
||||
|
||||
namespace PostIt.Controls;
|
||||
|
||||
public partial class MarkdownEditorControl : UserControl
|
||||
{
|
||||
public static readonly StyledProperty<string?> MarkdownTextProperty =
|
||||
AvaloniaProperty.Register<MarkdownEditorControl, string?>(nameof(MarkdownText));
|
||||
|
||||
public static readonly StyledProperty<TextDocument?> DocumentProperty =
|
||||
AvaloniaProperty.Register<MarkdownEditorControl, TextDocument?>(nameof(Document));
|
||||
|
||||
public static readonly StyledProperty<bool> IsPreviewVisibleProperty =
|
||||
AvaloniaProperty.Register<MarkdownEditorControl, bool>(nameof(IsPreviewVisible), false);
|
||||
|
||||
public string? MarkdownText
|
||||
{
|
||||
get => GetValue(MarkdownTextProperty);
|
||||
set => SetValue(MarkdownTextProperty, value);
|
||||
}
|
||||
|
||||
public TextDocument? Document
|
||||
{
|
||||
get => GetValue(DocumentProperty);
|
||||
set => SetValue(DocumentProperty, value);
|
||||
}
|
||||
|
||||
public bool IsPreviewVisible
|
||||
{
|
||||
get => GetValue(IsPreviewVisibleProperty);
|
||||
set => SetValue(IsPreviewVisibleProperty, value);
|
||||
}
|
||||
|
||||
public MarkdownEditorControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == MarkdownTextProperty)
|
||||
{
|
||||
var text = e.NewValue as string ?? string.Empty;
|
||||
if (Document is null || !string.Equals(Document.Text, text, StringComparison.Ordinal))
|
||||
{
|
||||
Document = new TextDocument(text);
|
||||
}
|
||||
}
|
||||
else if (e.Property == DocumentProperty)
|
||||
{
|
||||
var newDocument = e.NewValue as TextDocument;
|
||||
var text = newDocument?.Text ?? string.Empty;
|
||||
if (!string.Equals(MarkdownText, text, StringComparison.Ordinal))
|
||||
{
|
||||
MarkdownText = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,18 +39,56 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel
|
|||
/// <summary>Editor buffer for the post body. Same pattern as
|
||||
/// <see cref="DraftTitle"/>.</summary>
|
||||
|
||||
public string DraftArticle
|
||||
public string? DraftArticle
|
||||
{
|
||||
get => DraftArticleDocument.Text;
|
||||
set {
|
||||
get => DraftArticleDocument?.Text ?? null;
|
||||
set
|
||||
{
|
||||
var currentText = DraftArticleDocument?.Text;
|
||||
if (string.Equals(currentText, value, StringComparison.Ordinal))
|
||||
{
|
||||
OnPropertyChanged(nameof(DraftArticle));
|
||||
return;
|
||||
}
|
||||
|
||||
OnPropertyChanging(nameof(DraftArticle));
|
||||
DraftArticleDocument = new TextDocument(value);
|
||||
this.OnPropertyChanged(nameof(DraftArticle));
|
||||
DraftArticleDocument = value is not null ? new TextDocument(value) : null;
|
||||
OnPropertyChanged(nameof(DraftArticle));
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial TextDocument DraftArticleDocument {get; set;}
|
||||
private TextDocument? _draftArticleDocument;
|
||||
|
||||
public TextDocument? DraftArticleDocument
|
||||
{
|
||||
get => _draftArticleDocument;
|
||||
set
|
||||
{
|
||||
if (ReferenceEquals(_draftArticleDocument, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_draftArticleDocument is not null)
|
||||
{
|
||||
_draftArticleDocument.TextChanged -= DraftArticleDocument_TextChanged;
|
||||
}
|
||||
|
||||
_draftArticleDocument = value;
|
||||
|
||||
if (_draftArticleDocument is not null)
|
||||
{
|
||||
_draftArticleDocument.TextChanged += DraftArticleDocument_TextChanged;
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(DraftArticleDocument));
|
||||
}
|
||||
}
|
||||
|
||||
private void DraftArticleDocument_TextChanged(object? sender, EventArgs e)
|
||||
{
|
||||
OnPropertyChanged(nameof(DraftArticle));
|
||||
}
|
||||
|
||||
/// <summary>Editor buffer for the post's publication state.
|
||||
/// Reflects the server-side <c>IsPublished</c> flag (the
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@
|
|||
<Design.DataContext>
|
||||
<vm:BlogsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
|
||||
Margin="5,5,10,5" HorizontalAlignment="Stretch" BringIntoViewOnFocusChange="False"
|
||||
VerticalAlignment="Stretch">
|
||||
<StackPanel>
|
||||
<StackPanel Margin="5,5,10,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
|
||||
<WrapPanel x:Name="BlogsHeaderPanel"
|
||||
Margin="5"
|
||||
|
|
@ -87,7 +84,7 @@
|
|||
<PathIcon Data="{StaticResource PublishedIcon}" />
|
||||
</CommandBarToggleButton.Icon>
|
||||
</CommandBarToggleButton>
|
||||
<CommandBarToggleButton IsChecked="{Binding IsMarkdownViewerVisible, Mode=TwoWay}" Label="Markdown Viewer">
|
||||
<CommandBarToggleButton IsChecked="{Binding IsMarkdownViewerVisible, Mode=TwoWay}" Label="Aperçu">
|
||||
<CommandBarToggleButton.Icon>
|
||||
<PathIcon Data="{StaticResource MarkdownIcon}" />
|
||||
</CommandBarToggleButton.Icon>
|
||||
|
|
@ -137,26 +134,8 @@
|
|||
</ListBox>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Padding="8"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
IsVisible="{Binding IsMarkdownViewerVisible}">
|
||||
<md:MarkdownViewer Name="MarkdownViewer" MarkdownText="{Binding DraftArticle}" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Padding="8"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding !IsMarkdownViewerVisible}">
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
RowSpacing="10">
|
||||
|
|
@ -180,23 +159,13 @@
|
|||
PlaceholderText="Title"
|
||||
Text="{Binding DraftTitle, Mode=TwoWay}" />
|
||||
|
||||
|
||||
<AvaloniaEdit:TextEditor
|
||||
x:Name="DraftArticleTextEditor"
|
||||
<postitControls:MarkdownEditorControl
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Document="{Binding DraftArticleDocument, Mode=TwoWay}"
|
||||
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"
|
||||
ShowLineNumbers="True"
|
||||
WordWrap="True"
|
||||
BorderThickness="1"
|
||||
BorderBrush="LightGray"
|
||||
CornerRadius="5"
|
||||
Padding="5,5,5,15"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
TextInputOptions.Multiline="True"
|
||||
TextInputOptions.ReturnKeyType="Default"
|
||||
/>
|
||||
IsPreviewVisible="{Binding IsMarkdownViewerVisible, Mode=TwoWay}"
|
||||
MarkdownText="{Binding DraftArticle, Mode=TwoWay}"
|
||||
Document="{Binding DraftArticleDocument, Mode=TwoWay}" />
|
||||
|
||||
<Border x:Name="AttachmentsBorder"
|
||||
Grid.Row="3"
|
||||
|
|
@ -220,8 +189,6 @@
|
|||
<postitControls:StatusBar Grid.Row="4" DataContext="{Binding ActionStatus}" x:Name="StatusBar" />
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</ContentPage>
|
||||
|
|
|
|||
Loading…
Reference in a new issue