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;
|
||||
|
||||
<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
|
||||
|
|
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