Make tabbed text editor, styled somewhat after VS Code
This commit is contained in:
parent
66adc34ee2
commit
7629d6e951
2 changed files with 183 additions and 20 deletions
|
@ -1,6 +1,6 @@
|
||||||
@using LANCommander.Extensions;
|
@using LANCommander.Extensions;
|
||||||
|
|
||||||
<GridRow>
|
<GridRow Class="text-editor">
|
||||||
<GridCol Span="6">
|
<GridCol Span="6">
|
||||||
<Tree TItem="FileTreeNode"
|
<Tree TItem="FileTreeNode"
|
||||||
DataSource="FileTree"
|
DataSource="FileTree"
|
||||||
|
@ -12,13 +12,12 @@
|
||||||
</GridCol>
|
</GridCol>
|
||||||
|
|
||||||
<GridCol Span="18">
|
<GridCol Span="18">
|
||||||
<Breadcrumb>
|
<Tabs Type="@TabType.EditableCard" HideAdd @bind-ActiveKey="ActiveFile" OnClose="OnTabClose">
|
||||||
@foreach (var part in CurrentFile.Split(Path.DirectorySeparatorChar))
|
@foreach (var file in OpenFiles)
|
||||||
{
|
{
|
||||||
<BreadcrumbItem>@part</BreadcrumbItem>
|
<TextEditorPane FilePath="@file" />
|
||||||
}
|
}
|
||||||
</Breadcrumb>
|
</Tabs>
|
||||||
<StandaloneCodeEditor @ref="Editor" Id="editor" ConstructionOptions="EditorConstructionOptions" />
|
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</GridRow>
|
</GridRow>
|
||||||
|
|
||||||
|
@ -26,6 +25,73 @@
|
||||||
.monaco-editor-container {
|
.monaco-editor-container {
|
||||||
min-height: 600px;
|
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>
|
</style>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -37,6 +103,9 @@
|
||||||
HashSet<FileTreeNode> FileTree { get; set; } = new HashSet<FileTreeNode>();
|
HashSet<FileTreeNode> FileTree { get; set; } = new HashSet<FileTreeNode>();
|
||||||
string CurrentFile = "";
|
string CurrentFile = "";
|
||||||
|
|
||||||
|
List<string> OpenFiles = new List<string>();
|
||||||
|
string ActiveFile;
|
||||||
|
|
||||||
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
|
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
|
||||||
{
|
{
|
||||||
return new StandaloneEditorConstructionOptions
|
return new StandaloneEditorConstructionOptions
|
||||||
|
@ -57,26 +126,27 @@
|
||||||
|
|
||||||
root.PopulateChildren(WorkingDirectory);
|
root.PopulateChildren(WorkingDirectory);
|
||||||
|
|
||||||
FileTree.Add(root);
|
foreach (var child in root.Children)
|
||||||
|
{
|
||||||
|
FileTree.Add(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenFile(FileTreeNode node)
|
private void OpenFile(FileTreeNode node)
|
||||||
{
|
{
|
||||||
if (File.Exists(node.FullName))
|
if (!OpenFiles.Contains(node.FullName))
|
||||||
{
|
OpenFiles.Add(node.FullName);
|
||||||
var file = new FileInfo(node.FullName);
|
|
||||||
|
|
||||||
CurrentFile = node.FullName;
|
ActiveFile = node.FullName;
|
||||||
|
|
||||||
if (!file.IsBinaryFile())
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTabClose(string key)
|
||||||
{
|
{
|
||||||
Editor.SetValue(File.ReadAllText(file.FullName));
|
OpenFiles.Remove(key);
|
||||||
}
|
|
||||||
else
|
StateHasChanged();
|
||||||
{
|
|
||||||
Editor.SetValue("This file cannot be edited because it is a binary file.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FileTreeNode
|
public class FileTreeNode
|
||||||
|
|
93
LANCommander/Pages/Servers/Components/TextEditorPane.razor
Normal file
93
LANCommander/Pages/Servers/Components/TextEditorPane.razor
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue