Make tabbed text editor, styled somewhat after VS Code

This commit is contained in:
Pat Hartl 2023-04-18 20:17:50 -05:00
parent 66adc34ee2
commit 7629d6e951
2 changed files with 183 additions and 20 deletions

View file

@ -1,6 +1,6 @@
@using LANCommander.Extensions;
<GridRow>
<GridRow Class="text-editor">
<GridCol Span="6">
<Tree TItem="FileTreeNode"
DataSource="FileTree"
@ -12,13 +12,12 @@
</GridCol>
<GridCol Span="18">
<Breadcrumb>
@foreach (var part in CurrentFile.Split(Path.DirectorySeparatorChar))
<Tabs Type="@TabType.EditableCard" HideAdd @bind-ActiveKey="ActiveFile" OnClose="OnTabClose">
@foreach (var file in OpenFiles)
{
<BreadcrumbItem>@part</BreadcrumbItem>
<TextEditorPane FilePath="@file" />
}
</Breadcrumb>
<StandaloneCodeEditor @ref="Editor" Id="editor" ConstructionOptions="EditorConstructionOptions" />
</Tabs>
</GridCol>
</GridRow>
@ -26,6 +25,73 @@
.monaco-editor-container {
min-height: 600px;
}
.text-editor {
background: #252525;
min-height: 600px;
}
.text-editor .ant-tree {
background: none;
color: #ccc;
padding-top: 16px;
padding-left: 8px;
}
.text-editor .ant-tree-node-content-wrapper:hover {
background: none;
}
.text-editor .ant-tree-treenode:hover {
background: #37373d;
}
.text-editor .text-editor-info-bar {
padding-left: 16px;
padding-right: 16px;
margin-top: 0;
margin-bottom: 0;
padding-top: 8px;
padding-bottom: 8px;
background: #1e1e1e;
}
.text-editor .text-editor-info-bar .ant-breadcrumb,
.text-editor .ant-breadcrumb-separator,
.text-editor .ant-breadcrumb li:last-child {
color: #a9a9a9;
}
.text-editor .ant-tabs-card.ant-tabs-top > .ant-tabs-nav .ant-tabs-tab,
.text-editor .ant-tabs-card.ant-tabs-top > div > .ant-tabs-nav .ant-tabs-tab {
border-radius: 0;
background: #2d2d2d;
padding: 4px 10px;
color: #a9a9a9;
border: none;
}
.text-editor .ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active,
.text-editor .ant-tabs-card > div > .ant-tabs-nav .ant-tabs-tab-active {
background: #1e1e1e !important;
}
.text-editor .ant-tabs-tab .ant-tabs-tab-remove .anticon {
color: #a9a9a9;
}
.text-editor .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn,
.text-editor .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-remove .anticon {
color: #fff;
}
.text-editor .ant-tabs-top > .ant-tabs-nav::before {
border: none;
}
.text-editor .ant-tabs-nav {
margin: 0;
}
</style>
@code {
@ -37,6 +103,9 @@
HashSet<FileTreeNode> FileTree { get; set; } = new HashSet<FileTreeNode>();
string CurrentFile = "";
List<string> OpenFiles = new List<string>();
string ActiveFile;
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
{
return new StandaloneEditorConstructionOptions
@ -57,26 +126,27 @@
root.PopulateChildren(WorkingDirectory);
FileTree.Add(root);
foreach (var child in root.Children)
{
FileTree.Add(child);
}
}
private void OpenFile(FileTreeNode node)
{
if (File.Exists(node.FullName))
{
var file = new FileInfo(node.FullName);
if (!OpenFiles.Contains(node.FullName))
OpenFiles.Add(node.FullName);
CurrentFile = node.FullName;
ActiveFile = node.FullName;
if (!file.IsBinaryFile())
{
Editor.SetValue(File.ReadAllText(file.FullName));
}
else
{
Editor.SetValue("This file cannot be edited because it is a binary file.");
}
}
StateHasChanged();
}
private void OnTabClose(string key)
{
OpenFiles.Remove(key);
StateHasChanged();
}
public class FileTreeNode

View file

@ -0,0 +1,93 @@
@using LANCommander.Extensions;
@if (FileInfo != null)
{
<TabPane Key="@FileInfo.FullName" ForceRender Tab="@FileInfo.Name">
<ChildContent>
<GridRow Align="middle" Class="text-editor-info-bar">
<GridCol Flex=@("auto")>
<Breadcrumb>
@foreach (var part in FileInfo.FullName.Split(Path.DirectorySeparatorChar))
{
<BreadcrumbItem>@part</BreadcrumbItem>
}
</Breadcrumb>
</GridCol>
<GridCol>
<Button Type="@ButtonType.Primary" OnClick="async () => await Save()" Disabled="!HasUnsavedChanges">Save</Button>
</GridCol>
</GridRow>
<StandaloneCodeEditor @ref="Editor" CssClass="text-editor" ConstructionOptions="EditorConstructionOptions" OnDidChangeModelContent="OnChange" />
</ChildContent>
</TabPane>
}
@code {
[Parameter] public string FilePath { get; set; }
TabPane Pane;
FileInfo FileInfo;
StandaloneCodeEditor Editor;
string Contents;
bool HasUnsavedChanges = false;
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
{
return new StandaloneEditorConstructionOptions
{
AutomaticLayout = true,
Theme = "vs-dark"
};
}
protected override async Task OnParametersSetAsync()
{
FileInfo = new FileInfo(FilePath);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (Editor != null && firstRender)
{
if (File.Exists(FileInfo.FullName))
{
if (!FileInfo.IsBinaryFile())
{
Contents = await File.ReadAllTextAsync(FileInfo.FullName);
await Editor.SetValue(Contents);
}
else
{
await Editor.SetValue("This file cannot be edited because it is a binary file.");
}
}
await Editor.AddCommand((int)BlazorMonaco.KeyMod.CtrlCmd | (int)BlazorMonaco.KeyCode.KeyS, async (editor) =>
{
await Save();
}, null);
}
}
private void OnChange(ModelContentChangedEvent eventArgs)
{
if (!HasUnsavedChanges)
{
HasUnsavedChanges = true;
StateHasChanged();
}
}
private async Task Save()
{
await File.WriteAllTextAsync(FileInfo.FullName, await Editor.GetValue());
HasUnsavedChanges = false;
StateHasChanged();
}
}