diff options
Diffstat (limited to '')
-rw-r--r-- | MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj | 1 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/Pages/About.razor | 32 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/Pages/Rooms/Create.razor | 450 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/Shared/ModalWindow.razor | 93 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/Shared/ModalWindow.razor.css | 70 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/wwwroot/index.html | 2 |
6 files changed, 357 insertions, 291 deletions
diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj index 4bb7aeb..5e72471 100644 --- a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj +++ b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj @@ -11,6 +11,7 @@ <PackageReference Include="Blazored.SessionStorage" Version="2.3.0" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.7" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.7" PrivateAssets="all" /> + <PackageReference Include="XtermBlazor" Version="1.9.0" /> </ItemGroup> <ItemGroup> diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor index cf43c4f..b8d9c4a 100644 --- a/MatrixRoomUtils.Web/Pages/About.razor +++ b/MatrixRoomUtils.Web/Pages/About.razor @@ -1,7 +1,9 @@ @page "/About" @using System.Net +@using System.Net.Sockets @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage +@using XtermBlazor <PageTitle>About</PageTitle> @@ -20,6 +22,9 @@ <p>This deployment also serves a copy of the compiled, hosting-ready binaries at <a href="MRU-SRC.tar.xz">/MRU-SRC.tar.xz</a>!</p> } +<Xterm @ref="_terminal" Options="_options" OnFirstRender="@OnFirstRender" style="max-width: fit-content; overflow-x: hidden;"/> + + @code { private bool showBinDownload { get; set; } @@ -34,4 +39,29 @@ await base.OnInitializedAsync(); } -} \ No newline at end of file + + private Xterm _terminal; + + private TerminalOptions _options = new TerminalOptions + { + CursorBlink = true, + CursorStyle = CursorStyle.Block, + Theme = + { + Background = "#17615e", + }, + }; + + private async Task OnFirstRender() { + var message = "Hello, World!\nThis is a terminal emulator!\n\nYou can type stuff here, and it will be sent to the server!\n\nThis is a test of the emergency broadcast system.\n\nThis is only a t"; + _terminal.Options.RendererType = RendererType.Dom; + _terminal.Options.ScreenReaderMode = true; + TcpClient. + for (var i = 0; i < message.Length; i++) { + await _terminal.Write(message[i].ToString()); + + await Task.Delay(50); + _terminal.Options.Theme.Background = $"#{(i * 2):X6}"; + } + } +} diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor b/MatrixRoomUtils.Web/Pages/Rooms/Create.razor index 4255424..3a98801 100644 --- a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor +++ b/MatrixRoomUtils.Web/Pages/Rooms/Create.razor @@ -12,213 +12,228 @@ <h3>Room Manager - Create Room</h3> @* <pre Contenteditable="true" @onkeypress="@JsonChanged" content="JsonString">@JsonString</pre> *@ -<style> - table.table-top-first-tr tr td:first-child { - vertical-align: top; - } +<style> + table.table-top-first-tr tr td:first-child { + vertical-align: top; + } </style> <table class="table-top-first-tr"> - <tr> - <td style="padding-bottom: 16px;">Preset:</td> - <td style="padding-bottom: 16px;"> - <InputSelect @bind-Value="@RoomPreset"> - @foreach (var createRoomRequest in Presets) { - <option value="@createRoomRequest.Key">@createRoomRequest.Key</option> - } - </InputSelect> - </td> - </tr> - <tr> - <td>Room name:</td> - <td> - <FancyTextBox @bind-Value="@creationEvent.Name"></FancyTextBox> - </td> - </tr> - <tr> - <td>Room alias (localpart):</td> - <td> - <FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox> - </td> - </tr> - <tr> - <td>Room type:</td> - <td> - <InputSelect @bind-Value="@creationEvent._creationContentBaseType.Type"> - <option value="">Room</option> - <option value="m.space">Space</option> - </InputSelect> - <FancyTextBox @bind-Value="@creationEvent._creationContentBaseType.Type"></FancyTextBox> - </td> - </tr> - <tr> - <td style="padding-top: 16px;">History visibility:</td> - <td style="padding-top: 16px;"> - @{ - var historyVisibility = creationEvent["m.room.history_visibility"].TypedContent as HistoryVisibilityEventData; - } - <InputSelect @bind-Value="@historyVisibility.HistoryVisibility"> - <option value="invited">Invited</option> - <option value="joined">Joined</option> - <option value="shared">Shared</option> - <option value="world_readable">World readable</option> - </InputSelect> - </td> - </tr> - <tr> - <td>Guest access:</td> + <tr style="padding-bottom: 16px;"> + <td>Preset:</td> <td> - @{ - var guestAccessEvent = creationEvent["m.room.guest_access"].TypedContent as GuestAccessEventData; + @if (Presets is null) { + <p style="color: red;">Presets is null!</p> } - <ToggleSlider @bind-Value="guestAccessEvent.IsGuestAccessEnabled"> - @(guestAccessEvent.IsGuestAccessEnabled ? "Guests can join" : "Guests cannot join") (@guestAccessEvent.GuestAccess) - </ToggleSlider> - <InputSelect @bind-Value="@guestAccessEvent.GuestAccess"> - <option value="can_join">Can join</option> - <option value="forbidden">Forbidden</option> - </InputSelect> - </td> - </tr> - - <tr> - <td>Room icon:</td> - <td> - @{ - var roomAvatarEvent = creationEvent["m.room.avatar"].TypedContent as RoomAvatarEventData; + else { + <InputSelect @bind-Value="@RoomPreset"> + @foreach (var createRoomRequest in Presets) { + <option value="@createRoomRequest.Key">@createRoomRequest.Key</option> + } + </InputSelect> } - <img src="@MediaResolver.ResolveMediaUri(HomeServer.HomeServerDomain, roomAvatarEvent.Url)" style="width: 128px; height: 128px; border-radius: 50%;"/> - <div style="display: inline-block; vertical-align: middle;"> - <FancyTextBox @bind-Value="@roomAvatarEvent.Url"></FancyTextBox><br/> - <InputFile OnChange="RoomIconFilePicked"></InputFile> - </div> - - </td> - </tr> - <tr> - <td>Permissions:</td> - <details> - <summary>@creationEvent.PowerLevelContentOverride.Users.Count members</summary> - @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) { - var _event = user; - <tr> - <td> - <FancyTextBox Formatter="@GetPermissionFriendlyName" - Value="@_event" - ValueChanged="val => { creationEvent.PowerLevelContentOverride.Events.ChangeKey(_event, val); }"> - </FancyTextBox>: - </td> - <td> - <input type="number" value="@creationEvent.PowerLevelContentOverride.Events[_event]" @oninput="val => { creationEvent.PowerLevelContentOverride.Events[_event] = int.Parse(val.Value.ToString()); }" @onfocusout="() => { creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"/> - </td> - </tr> - } - @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) { - var _user = user; - <tr> - <td><FancyTextBox Value="@_user" ValueChanged="val => { creationEvent.PowerLevelContentOverride.Users.ChangeKey(_user, val); creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"></FancyTextBox>:</td> - <td> - <input type="number" value="@creationEvent.PowerLevelContentOverride.Users[_user]" @oninput="val => { creationEvent.PowerLevelContentOverride.Users[_user] = int.Parse(val.Value.ToString()); }"/> - </td> - </tr> - } - </details> - </tr> - <tr> - <td>Server ACLs:</td> - <td> - @{ - var serverAcl = creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData; - } - <details> - <summary>@((creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).Allow.Count) allow rules</summary> - <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> - </details> - <details> - <summary>@(creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).Deny.Count deny rules</summary> - <StringListEditor @bind-Items="@serverAcl.Deny"></StringListEditor> - </details> </td> </tr> + @if (creationEvent is not null) { + <tr> + <td>Room name:</td> + <td> + @if (creationEvent.Name is null) { + <p style="color: red;">creationEvent.Name is null!</p> + } + else { + <FancyTextBox @bind-Value="@creationEvent.Name"></FancyTextBox> + <p>(#<FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox>:@HomeServer.WhoAmI.UserId.Split(':').Last())</p> + } + </td> + </tr> + <tr> + <td>Room type:</td> + <td> + @if (creationEvent._creationContentBaseType is null) { + <p style="color: red;">creationEvent._creationContentBaseType is null!</p> + } + else { + <InputSelect @bind-Value="@creationEvent._creationContentBaseType.Type"> + <option value="">Room</option> + <option value="m.space">Space</option> + </InputSelect> + <FancyTextBox @bind-Value="@creationEvent._creationContentBaseType.Type"></FancyTextBox> + } + </td> + </tr> + <tr> + <td style="padding-top: 16px;">History visibility:</td> + <td style="padding-top: 16px;"> + <InputSelect @bind-Value="@historyVisibility.HistoryVisibility"> + <option value="invited">Invited</option> + <option value="joined">Joined</option> + <option value="shared">Shared</option> + <option value="world_readable">World readable</option> + </InputSelect> + </td> + </tr> + <tr> + <td>Guest access:</td> + <td> + <ToggleSlider @bind-Value="guestAccessEvent.IsGuestAccessEnabled"> + @(guestAccessEvent.IsGuestAccessEnabled ? "Guests can join" : "Guests cannot join") (@guestAccessEvent.GuestAccess) + </ToggleSlider> + <InputSelect @bind-Value="@guestAccessEvent.GuestAccess"> + <option value="can_join">Can join</option> + <option value="forbidden">Forbidden</option> + </InputSelect> + </td> + </tr> - <tr> - <td>Invited members:</td> - <td> + <tr> + <td>Room icon:</td> + <td> + <img src="@MediaResolver.ResolveMediaUri(HomeServer.HomeServerDomain, roomAvatarEvent.Url)" style="width: 128px; height: 128px; border-radius: 50%;"/> + <div style="display: inline-block; vertical-align: middle;"> + <FancyTextBox @bind-Value="@roomAvatarEvent.Url"></FancyTextBox><br/> + <InputFile OnChange="RoomIconFilePicked"></InputFile> + </div> + </td> + </tr> + <tr> + <td>Permissions:</td> <details> - <summary>@creationEvent.InitialState.Count(x => x.Type == "m.room.member") members</summary> - @* <button @onclick="() => { RuntimeCache.LoginSessions.Select(x => x.Value.LoginResponse.UserId).ToList().ForEach(InviteMember); }">Invite all logged in accounts</button> *@ - @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != HomeServer.UserId)) { - <UserListItem UserId="@member.StateKey"></UserListItem> + <summary>@creationEvent.PowerLevelContentOverride.Users.Count members</summary> + @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) { + var _event = user; + <tr> + <td> + <FancyTextBox Formatter="@GetPermissionFriendlyName" + Value="@_event" + ValueChanged="val => { creationEvent.PowerLevelContentOverride.Events.ChangeKey(_event, val); }"> + </FancyTextBox>: + </td> + <td> + <input type="number" value="@creationEvent.PowerLevelContentOverride.Events[_event]" @oninput="val => { creationEvent.PowerLevelContentOverride.Events[_event] = int.Parse(val.Value.ToString()); }" @onfocusout="() => { creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"/> + </td> + </tr> + } + @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) { + var _user = user; + <tr> + <td><FancyTextBox Value="@_user" ValueChanged="val => { creationEvent.PowerLevelContentOverride.Users.ChangeKey(_user, val); creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"></FancyTextBox>:</td> + <td> + <input type="number" value="@creationEvent.PowerLevelContentOverride.Users[_user]" @oninput="val => { creationEvent.PowerLevelContentOverride.Users[_user] = int.Parse(val.Value.ToString()); }"/> + </td> + </tr> } </details> - </td> - </tr> - @* Initial states, should remain at bottom *@ - <tr> - <td style="vertical-align: top;">Initial states:</td> - <td> - <details> - - @code - { - private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; + </tr> + <tr> + <td>Server ACLs:</td> + <td> + @if (serverAcl?.Allow is null) { + <p>No allow rules exist!</p> + <button @onclick="@(() => { serverAcl.Allow = new() { "*" }; })">Create sane defaults</button> } + else { + <details> + <summary>@((creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).Allow.Count) allow rules</summary> + @* <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> *@ + </details> + } + @if (serverAcl?.Deny is null) { + <p>No deny rules exist!</p> + <button @onclick="@(() => { serverAcl.Allow = new(); })">Create sane defaults</button> + } + else { + <details> + <summary>@((creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).Deny.Count) deny rules</summary> + @* <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> *@ + </details> + } + </td> + </tr> - <summary> @creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states</summary> - <table> - @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) { - <tr> - <td style="vertical-align: top;"> - @(initialState.Type): - @if (!string.IsNullOrEmpty(initialState.StateKey)) { - <br/> - <span>(@initialState.StateKey)</span> - } - - </td> - <td> - <pre>@JsonSerializer.Serialize(initialState.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre> - </td> - </tr> + <tr> + <td>Invited members:</td> + <td> + <details> + <summary>@creationEvent.InitialState.Count(x => x.Type == "m.room.member") members</summary> + @* <button @onclick="() => { RuntimeCache.LoginSessions.Select(x => x.Value.LoginResponse.UserId).ToList().ForEach(InviteMember); }">Invite all logged in accounts</button> *@ + @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != HomeServer.UserId)) { + <UserListItem UserId="@member.StateKey"></UserListItem> } - </table> - </details> - <details> - <summary> @creationEvent.InitialState.Count initial states</summary> - <table> - @foreach (var initialState in creationEvent.InitialState) { - var _state = initialState; - <tr> - <td style="vertical-align: top;"> - <span>@(_state.Type):</span><br/> - <button @onclick="() => { creationEvent.InitialState.Remove(_state); StateHasChanged(); }">Remove</button> - </td> + </details> + </td> + </tr> + @* Initial states, should remain at bottom *@ + <tr> + <td style="vertical-align: top;">Initial states:</td> + <td> + <details> - <td> - <pre>@JsonSerializer.Serialize(_state.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre> - </td> - </tr> + @code + { + private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; } - </table> - </details> - </td> - </tr> + + <summary> @creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states</summary> + <table> + @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) { + <tr> + <td style="vertical-align: top;"> + @(initialState.Type): + @if (!string.IsNullOrEmpty(initialState.StateKey)) { + <br/> + <span>(@initialState.StateKey)</span> + } + + </td> + <td> + <pre>@JsonSerializer.Serialize(initialState.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre> + </td> + </tr> + } + </table> + </details> + <details> + <summary> @creationEvent.InitialState.Count initial states</summary> + <table> + @foreach (var initialState in creationEvent.InitialState) { + var _state = initialState; + <tr> + <td style="vertical-align: top;"> + <span>@(_state.Type):</span><br/> + <button @onclick="() => { creationEvent.InitialState.Remove(_state); StateHasChanged(); }">Remove</button> + </td> + + <td> + <pre>@JsonSerializer.Serialize(_state.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre> + </td> + </tr> + } + </table> + </details> + </td> + </tr> } </table> <button @onclick="CreateRoom">Create room</button> <br/> -<details> - <summary>Creation JSON</summary> +<ModalWindow Title="Creation JSON"> <pre> - @creationEvent.ToJson(ignoreNull: true) - </pre> -</details> -<details open> - <summary>Creation JSON (with null values)</summary> + @creationEvent.ToJson(ignoreNull: true) + </pre> +</ModalWindow> +<ModalWindow Title="Creation JSON (with null values)"> <pre> - @creationEvent.ToJson() - </pre> -</details> + @creationEvent.ToJson() + </pre> +</ModalWindow> +@if (_matrixException is not null) { + <ModalWindow Title="@("Matrix exception: " + _matrixException.ErrorCode)"> + <pre> + @_matrixException.Message + </pre> + </ModalWindow> +} @code { @@ -232,22 +247,22 @@ set { creationEvent = Presets[value]; JsonChanged(); - - creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - guestAccessEvent = creationEvent["m.room.guest_access"].TypedContent as GuestAccessEventData; StateHasChanged(); } } - private Dictionary<string, string> creationEventValidationErrors { get; set; } = new(); + private CreateRoomRequest? creationEvent { get; set; } - private CreateRoomRequest creationEvent { get; set; } - GuestAccessEventData guestAccessEvent { get; set; } - - private Dictionary<string, CreateRoomRequest> Presets { get; set; } = new(); + private Dictionary<string, CreateRoomRequest>? Presets { get; set; } = new(); private AuthenticatedHomeServer? HomeServer { get; set; } + private MatrixException? _matrixException { get; set; } + + private HistoryVisibilityEventData? historyVisibility => creationEvent?["m.room.history_visibility"].TypedContent as HistoryVisibilityEventData; + private GuestAccessEventData? guestAccessEvent => creationEvent?["m.room.guest_access"].TypedContent as GuestAccessEventData; + private ServerACLEventData? serverAcl => creationEvent?["m.room.server_acls"].TypedContent as ServerACLEventData; + private RoomAvatarEventData? roomAvatarEvent => creationEvent?["m.room.avatar"].TypedContent as RoomAvatarEventData; + protected override async Task OnInitializedAsync() { HomeServer = await MRUStorage.GetCurrentSessionOrNavigate(); if (HomeServer is null) return; @@ -280,7 +295,12 @@ Console.WriteLine("Create room"); Console.WriteLine(creationEvent.ToJson()); creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixRoomUtils (https://mru.rory.gay)"); - var id = await HomeServer.CreateRoom(creationEvent); + try { + var id = await HomeServer.CreateRoom(creationEvent); + } + catch (MatrixException e) { + _matrixException = e; + } } private void InviteMember(string mxid) { @@ -295,28 +315,28 @@ }); } - private string GetStateFriendlyName(string key) => key switch { - "m.room.history_visibility" => "History visibility", - "m.room.guest_access" => "Guest access", - "m.room.join_rules" => "Join rules", - "m.room.server_acl" => "Server ACL", - "m.room.avatar" => "Avatar", - _ => key + private string GetStateFriendlyName(string key) => key switch { + "m.room.history_visibility" => "History visibility", + "m.room.guest_access" => "Guest access", + "m.room.join_rules" => "Join rules", + "m.room.server_acl" => "Server ACL", + "m.room.avatar" => "Avatar", + _ => key }; - private string GetPermissionFriendlyName(string key) => key switch { - "m.reaction" => "Send reaction", - "m.room.avatar" => "Change room icon", - "m.room.canonical_alias" => "Change room alias", - "m.room.encryption" => "Enable encryption", - "m.room.history_visibility" => "Change history visibility", - "m.room.name" => "Change room name", - "m.room.power_levels" => "Change power levels", - "m.room.tombstone" => "Upgrade room", - "m.room.topic" => "Change room topic", - "m.room.pinned_events" => "Pin events", - "m.room.server_acl" => "Change server ACLs", - _ => key + private string GetPermissionFriendlyName(string key) => key switch { + "m.reaction" => "Send reaction", + "m.room.avatar" => "Change room icon", + "m.room.canonical_alias" => "Change room alias", + "m.room.encryption" => "Enable encryption", + "m.room.history_visibility" => "Change history visibility", + "m.room.name" => "Change room name", + "m.room.power_levels" => "Change power levels", + "m.room.tombstone" => "Upgrade room", + "m.room.topic" => "Change room topic", + "m.room.pinned_events" => "Pin events", + "m.room.server_acl" => "Change server ACLs", + _ => key }; -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Web/Shared/ModalWindow.razor b/MatrixRoomUtils.Web/Shared/ModalWindow.razor index 75c2933..216f1f3 100644 --- a/MatrixRoomUtils.Web/Shared/ModalWindow.razor +++ b/MatrixRoomUtils.Web/Shared/ModalWindow.razor @@ -1,95 +1,37 @@ <div class="r-modal" style="top: @(_y)px; left: @(_x)px;"> <div class="titlebar" @onmousedown="MouseDown" @onmouseup="MouseUp" @onmousemove="MouseMove" @onmouseleave="MouseMove"> <b class="title">@Title</b> - <button class="close" @onclick="OnCloseClicked">X</button> - </div> - <div class="content"> - @ChildContent + <button class="btnclose" @onclick="OnCloseClicked">X</button> + <button class="btncollapse" @onclick="@(() => Collapsed = !Collapsed)">_</button> </div> + <div class="content" style="@(Collapsed ? "height: 0px;" : "")"> + @ChildContent + </div> </div> -<style> - .r-modal { - position: absolute; - width: fit-content; - height: fit-content; - z-index: 1000; - } - .r-modal:hover { - z-index: 1001; - } - - .r-modal > .titlebar { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 25px; - background-color: #000; - user-select: none; - } - - .r-modal > .titlebar > .title { - position: relative; - top: 0; - left: 0; - width: fit-content; - height: 100%; - line-height: 25px; - padding-left: 10px; - color: #fff; - } - - .r-modal > .titlebar > .close { - position: absolute; - top: 0; - right: 0; - width: 25px; - height: 100%; - line-height: 25px; - text-align: center; - color: #fff; - background-color: #111; - cursor: pointer; - } - - .r-modal > .content { - position: relative; - top: 25px; - left: 0; - width: fit-content; - height: fit-content; - min-width: 150px; - min-height: 5px; - max-width: 75vw; - max-height: 75vh; - overflow: auto; - background-color: #fff; - } -</style> - @code { - + [Parameter] public RenderFragment? ChildContent { get; set; } - + [Parameter] public string Title { get; set; } = "Untitled window"; - + [Parameter] public double X { get; set; } = 60; - + [Parameter] public double Y { get; set; } = 60; - + [Parameter] public Action OnCloseClicked { get; set; } - + + [Parameter] + public bool Collapsed { get; set; } = false; private double _x = 60; private double _y = 60; - + protected override void OnInitialized() { _x = X; _y = Y; @@ -97,16 +39,17 @@ private void WindowDrag(DragEventArgs obj) { Console.WriteLine("Drag: " + obj.ToJson()); - + _x += obj.MovementX; _y += obj.MovementY; - + StateHasChanged(); } private bool isDragging = false; private double dragX = 0; private double dragY = 0; + private void MouseDown(MouseEventArgs obj) { isDragging = true; dragX = obj.ClientX; @@ -127,4 +70,4 @@ } } -} \ No newline at end of file +} diff --git a/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css b/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css new file mode 100644 index 0000000..b25ab0e --- /dev/null +++ b/MatrixRoomUtils.Web/Shared/ModalWindow.razor.css @@ -0,0 +1,70 @@ +.r-modal { + position: absolute; + width: fit-content; + height: fit-content; + z-index: 1000; +} +.r-modal:hover { + z-index: 1001; +} + +.r-modal > .titlebar { + position: absolute; + display: block; + top: 0; + left: 0; + width: 100%; + height: 25px; + background-color: #000; + user-select: none; +} + +.r-modal > .titlebar > .title { + position: relative; + top: 0; + left: 0; + width: fit-content; + height: 100%; + line-height: 25px; + padding-left: 10px; + color: #fff; +} + +.r-modal > .titlebar > .btnclose { + position: absolute; + top: 0; + right: 0; + width: 25px; + height: 100%; + line-height: 25px; + text-align: center; + color: #fff; + background-color: #111; + cursor: pointer; +} +.r-modal > .titlebar > .btncollapse { + position: absolute; + top: 0; + right: 25px; + width: 25px; + height: 100%; + line-height: 25px; + text-align: center; + color: #fff; + background-color: #111; + cursor: pointer; +} + +.r-modal > .content { + position: relative; + top: 25px; + left: 0; + width: fit-content; + height: fit-content; + min-width: 150px; + min-height: 5px; + max-width: 75vw; + max-height: 75vh; + overflow: auto; + background-color: #111; +} diff --git a/MatrixRoomUtils.Web/wwwroot/index.html b/MatrixRoomUtils.Web/wwwroot/index.html index 9c9e7d0..0598c4d 100644 --- a/MatrixRoomUtils.Web/wwwroot/index.html +++ b/MatrixRoomUtils.Web/wwwroot/index.html @@ -10,6 +10,8 @@ <link href="css/app.css" rel="stylesheet"/> <link href="favicon.png" rel="icon" type="image/png"/> <link href="MatrixRoomUtils.Web.styles.css" rel="stylesheet"/> + <link href="_content/XtermBlazor/XtermBlazor.css" rel="stylesheet" /> + <script src="_content/XtermBlazor/XtermBlazor.min.js"></script> </head> <body> |