[frontend] Refactor EmojiPicker and it's usages, add globally accessible components
This commit is contained in:
parent
641c6578e6
commit
52dca9f3be
12 changed files with 105 additions and 54 deletions
|
@ -10,7 +10,9 @@
|
||||||
@inject ComposeService ComposeService
|
@inject ComposeService ComposeService
|
||||||
@inject SessionService SessionService
|
@inject SessionService SessionService
|
||||||
@inject IStringLocalizer<Localization> Loc;
|
@inject IStringLocalizer<Localization> Loc;
|
||||||
<dialog class="compose" @ref="Dialog">
|
@inject GlobalComponentSvc GlobalComponentSvc
|
||||||
|
<dialog class="dialog" @ref="Dialog">
|
||||||
|
<div class="compose">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<button @onclick="CloseDialog">
|
<button @onclick="CloseDialog">
|
||||||
<Icon Name="Icons.X"/>
|
<Icon Name="Icons.X"/>
|
||||||
|
@ -31,7 +33,7 @@
|
||||||
<input @bind="NoteDraft.Cw" class="input cw-field" placeholder="Content Warning"/>
|
<input @bind="NoteDraft.Cw" class="input cw-field" placeholder="Content Warning"/>
|
||||||
<hr class="separator"/>
|
<hr class="separator"/>
|
||||||
}
|
}
|
||||||
<textarea @ref="Textarea" @bind="NoteDraft.Text" class="textarea" placeholder="@TextPlaceholder" rows="5" cols="35"></textarea>
|
<textarea @ref="Textarea" @bind="NoteDraft.Text" class="textarea" placeholder="@TextPlaceholder" rows="5" cols="35" autofocus="autofocus"></textarea>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<button class="footer-btn" @onclick="OpenUpload">
|
<button class="footer-btn" @onclick="OpenUpload">
|
||||||
<Icon Name="Icons.Upload" Size="1.3rem"></Icon>
|
<Icon Name="Icons.Upload" Size="1.3rem"></Icon>
|
||||||
|
@ -39,17 +41,18 @@
|
||||||
<button class="footer-btn" @onclick="ToggleCw">
|
<button class="footer-btn" @onclick="ToggleCw">
|
||||||
<Icon Name="Icons.EyeSlash" Size="1.3rem"></Icon>
|
<Icon Name="Icons.EyeSlash" Size="1.3rem"></Icon>
|
||||||
</button>
|
</button>
|
||||||
<button class="footer-btn" @onclick="ToggleEmojiPicker">
|
<button @ref="EmojiButton" class="footer-btn positioned" @onclick="ToggleEmojiPicker">
|
||||||
<Icon Name="Icons.Smiley" Size="1.3rem"></Icon>
|
<Icon Name="Icons.Smiley" Size="1.3rem"></Icon>
|
||||||
<EmojiPicker OnEmojiSelect="AddEmoji" />
|
|
||||||
</button>
|
</button>
|
||||||
<div class="file-input">
|
<div class="file-input">
|
||||||
<InputFile @ref="UploadInput" OnChange="Upload">Upload!</InputFile>
|
<InputFile @ref="UploadInput" OnChange="Upload">Upload!</InputFile>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div @onclick="CloseDialog" class="backdrop"></div>
|
<div @onclick="CloseDialog" class="backdrop"></div>
|
||||||
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference Dialog { get; set; }
|
private ElementReference Dialog { get; set; }
|
||||||
private IJSObjectReference _module = null!;
|
private IJSObjectReference _module = null!;
|
||||||
|
@ -58,7 +61,7 @@
|
||||||
private NoteBase? ReplyOrQuote { get; set; }
|
private NoteBase? ReplyOrQuote { get; set; }
|
||||||
private string? TextPlaceholder { get; set; }
|
private string? TextPlaceholder { get; set; }
|
||||||
private ElementReference Textarea { get; set; }
|
private ElementReference Textarea { get; set; }
|
||||||
private EmojiPicker EmojiPicker { get; set; } = null!;
|
private ElementReference EmojiButton { get; set; }
|
||||||
|
|
||||||
private NoteCreateRequest NoteDraft { get; set; } = new()
|
private NoteCreateRequest NoteDraft { get; set; } = new()
|
||||||
{
|
{
|
||||||
|
@ -250,9 +253,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleEmojiPicker()
|
private void ToggleEmojiPicker()
|
||||||
{
|
{
|
||||||
_ = EmojiPicker.Toggle();
|
GlobalComponentSvc.EmojiPicker?.Open(EmojiButton, new EventCallback<EmojiResponse>(this, AddEmoji));
|
||||||
StateHasChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddEmoji(EmojiResponse emoji)
|
private async Task AddEmoji(EmojiResponse emoji)
|
||||||
|
|
|
@ -4,11 +4,24 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
top: 10%;
|
top: 10%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
position: relative;
|
||||||
|
width: 45rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose::backdrop {
|
.dialog {
|
||||||
background-color: black;
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
margin: auto;
|
||||||
|
background-color: unset;
|
||||||
|
z-index: +1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backdrop {
|
||||||
opacity: 50%;
|
opacity: 50%;
|
||||||
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
@ -20,6 +33,7 @@
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
@ -61,11 +75,16 @@
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.positioned {
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.backdrop {
|
.backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: -1
|
z-index: -2
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
export function openDialog(element) {
|
export function openDialog(element) {
|
||||||
element.showModal()
|
element.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function closeDialog(element) {
|
export function closeDialog(element) {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
@using Iceshrimp.Frontend.Core.Services
|
@using Iceshrimp.Frontend.Core.Services
|
||||||
@using Iceshrimp.Shared.Schemas.Web
|
@using Iceshrimp.Shared.Schemas.Web
|
||||||
@inject EmojiService EmojiService
|
@inject EmojiService EmojiService
|
||||||
|
@inject GlobalComponentSvc GlobalComponentSvc
|
||||||
|
@inject IJSRuntime Js
|
||||||
|
|
||||||
@if (_display)
|
<dialog class="dialog" @ref="EmojiPickerRef">
|
||||||
{
|
<div class="emoji-picker" style="--top: @(_top)px; --left: @(_left)px">
|
||||||
<div @ref="EmojiPickerRef" @onfocusout="Close" class="emoji-picker @(Fixed ? "fixed" : "")" tabindex="0">
|
|
||||||
@foreach (var el in EmojiList)
|
@foreach (var el in EmojiList)
|
||||||
{
|
{
|
||||||
<div class="emoji">
|
<div class="emoji">
|
||||||
|
@ -12,45 +13,43 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
<ClosingBackdrop OnClose="Close"></ClosingBackdrop>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public bool Fixed { get; set; }
|
private EventCallback<EmojiResponse> OnEmojiSelect { get; set; }
|
||||||
[Parameter] [EditorRequired] public required EventCallback<EmojiResponse> OnEmojiSelect { get; set; }
|
private List<EmojiResponse> EmojiList { get; set; } = [];
|
||||||
|
private ElementReference EmojiPickerRef { get; set; }
|
||||||
private List<EmojiResponse> EmojiList { get; set; } = [];
|
private float _top;
|
||||||
private bool _display = false;
|
private float _left;
|
||||||
private ElementReference EmojiPickerRef { get; set; }
|
private IJSInProcessObjectReference _module = null!;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
EmojiList = await EmojiService.GetEmoji();
|
GlobalComponentSvc.EmojiPicker = this;
|
||||||
|
EmojiList = await EmojiService.GetEmoji();
|
||||||
|
_module = (IJSInProcessObjectReference)await Js.InvokeAsync<IJSObjectReference>("import",
|
||||||
|
"./Components/EmojiPicker.razor.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Select(EmojiResponse emoji)
|
private async void Select(EmojiResponse emoji)
|
||||||
{
|
{
|
||||||
await OnEmojiSelect.InvokeAsync(emoji);
|
await OnEmojiSelect.InvokeAsync(emoji);
|
||||||
_display = false;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Close()
|
private void Close()
|
||||||
{
|
{
|
||||||
_display = false;
|
_module.InvokeVoid("closeDialog", EmojiPickerRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Open(ElementReference root, EventCallback<EmojiResponse> func)
|
||||||
|
{
|
||||||
|
OnEmojiSelect = func;
|
||||||
|
var pos = _module.Invoke<List<float>>("getPosition", root);
|
||||||
|
_left = pos[0];
|
||||||
|
_top = pos[1];
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
_module.InvokeVoid("openDialog", EmojiPickerRef);
|
||||||
|
|
||||||
protected override void OnAfterRender(bool firstRender)
|
|
||||||
{
|
|
||||||
if (_display)
|
|
||||||
{
|
|
||||||
EmojiPickerRef.FocusAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Toggle()
|
|
||||||
{
|
|
||||||
_display = !_display;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
.emoji-picker {
|
.emoji-picker {
|
||||||
|
--top: 0px;
|
||||||
|
--left: 0px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(2.5rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(2.5rem, 1fr));
|
||||||
grid-template-rows: repeat(auto-fill, minmax(2.5rem, 1fr));
|
grid-template-rows: repeat(auto-fill, minmax(2.5rem, 1fr));
|
||||||
|
@ -10,9 +12,12 @@
|
||||||
min-width: 15rem;
|
min-width: 15rem;
|
||||||
min-height: 10rem;
|
min-height: 10rem;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
z-index: 100;
|
top: calc(var(--top) + 2.5rem);
|
||||||
top: 2.5rem;
|
left: calc(var(--left) - 5.5rem);
|
||||||
left: -6rem;
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
z-index: +2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji {
|
.emoji {
|
||||||
|
@ -22,4 +27,3 @@
|
||||||
width: 2rem;
|
width: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
Iceshrimp.Frontend/Components/EmojiPicker.razor.js
Normal file
14
Iceshrimp.Frontend/Components/EmojiPicker.razor.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export function getPosition(ref){
|
||||||
|
let rect = ref.getBoundingClientRect()
|
||||||
|
let x = rect.x + window.scrollX;
|
||||||
|
let y = rect.y + window.scrollY;
|
||||||
|
return [x, y]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openDialog(ref){
|
||||||
|
ref.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeDialog(ref){
|
||||||
|
ref.close();
|
||||||
|
}
|
4
Iceshrimp.Frontend/Components/GlobalComponents.razor
Normal file
4
Iceshrimp.Frontend/Components/GlobalComponents.razor
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<EmojiPicker></EmojiPicker>
|
||||||
|
@code {
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
@inject IStringLocalizer<Localization> Loc;
|
@inject IStringLocalizer<Localization> Loc;
|
||||||
@inject IJSRuntime Js;
|
@inject IJSRuntime Js;
|
||||||
@inject SessionService Session;
|
@inject SessionService Session;
|
||||||
|
@inject GlobalComponentSvc GlobalComponentSvc
|
||||||
|
|
||||||
<div class="note-footer">
|
<div class="note-footer">
|
||||||
@if (Reactions.Count > 0)
|
@if (Reactions.Count > 0)
|
||||||
|
@ -48,9 +49,8 @@
|
||||||
<span class="like-count">@Likes</span>
|
<span class="like-count">@Likes</span>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn positioned" @onclick="ToggleEmojiPicker" @onclick:stopPropagation="true">
|
<button @ref="EmojiButton" class="btn" @onclick="ToggleEmojiPicker" @onclick:stopPropagation="true">
|
||||||
<Icon Name="Icons.Smiley" Size="1.3em"/>
|
<Icon Name="Icons.Smiley" Size="1.3em"/>
|
||||||
<EmojiPicker @ref="EmojiPicker" OnEmojiSelect="React"/>
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn" @onclick="Quote" @onclick:stopPropagation="true">
|
<button class="btn" @onclick="Quote" @onclick:stopPropagation="true">
|
||||||
<Icon Name="Icons.Quotes" Size="1.3em"/>
|
<Icon Name="Icons.Quotes" Size="1.3em"/>
|
||||||
|
@ -80,8 +80,8 @@
|
||||||
[Parameter] [EditorRequired] public required bool IsLiked { get; set; }
|
[Parameter] [EditorRequired] public required bool IsLiked { get; set; }
|
||||||
[Parameter] [EditorRequired] public required int Renotes { get; set; }
|
[Parameter] [EditorRequired] public required int Renotes { get; set; }
|
||||||
[Parameter] public bool RenotePossible { get; set; }
|
[Parameter] public bool RenotePossible { get; set; }
|
||||||
private EmojiPicker EmojiPicker { get; set; } = null!;
|
|
||||||
private Menu Menu { get; set; } = null!;
|
private Menu Menu { get; set; } = null!;
|
||||||
|
private ElementReference EmojiButton { get; set; }
|
||||||
|
|
||||||
[CascadingParameter] Note Note { get; set; } = null!;
|
[CascadingParameter] Note Note { get; set; } = null!;
|
||||||
|
|
||||||
|
@ -111,9 +111,9 @@
|
||||||
Note.DoQuote();
|
Note.DoQuote();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ToggleEmojiPicker()
|
private void ToggleEmojiPicker()
|
||||||
{
|
{
|
||||||
await EmojiPicker.Toggle();
|
GlobalComponentSvc.EmojiPicker?.Open(EmojiButton, new EventCallback<EmojiResponse>(this, React));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void React(EmojiResponse emoji)
|
private void React(EmojiResponse emoji)
|
||||||
|
|
7
Iceshrimp.Frontend/Core/Services/GlobalComponentSvc.cs
Normal file
7
Iceshrimp.Frontend/Core/Services/GlobalComponentSvc.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
using Iceshrimp.Frontend.Components;
|
||||||
|
namespace Iceshrimp.Frontend.Core.Services;
|
||||||
|
|
||||||
|
public class GlobalComponentSvc
|
||||||
|
{
|
||||||
|
public EmojiPicker? EmojiPicker { get; set; }
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@inject NavigationManager Navigation;
|
@inject NavigationManager Navigation;
|
||||||
|
|
||||||
|
<GlobalComponents></GlobalComponents>
|
||||||
<div @ref="SidebarElementRef" @onfocusout="Close" class="sidebar @(_open ? "open" : "")" tabindex=0>
|
<div @ref="SidebarElementRef" @onfocusout="Close" class="sidebar @(_open ? "open" : "")" tabindex=0>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<account-dropdown/>
|
<account-dropdown/>
|
||||||
|
@ -110,4 +111,4 @@
|
||||||
{
|
{
|
||||||
Navigation.LocationChanged -= HandleLocationChanged;
|
Navigation.LocationChanged -= HandleLocationChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 15rem;
|
width: 15rem;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
z-index: 200;
|
z-index: +3;
|
||||||
background-color: var(--foreground-color);
|
background-color: var(--foreground-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,6 @@
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
background-color: var(--foreground-color);
|
background-color: var(--foreground-color);
|
||||||
z-index: 999;
|
z-index: +4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ builder.Services.AddSingleton<StateService>();
|
||||||
builder.Services.AddSingleton<EmojiService>();
|
builder.Services.AddSingleton<EmojiService>();
|
||||||
builder.Services.AddSingleton<VersionService>();
|
builder.Services.AddSingleton<VersionService>();
|
||||||
builder.Services.AddSingleton<MessageService>();
|
builder.Services.AddSingleton<MessageService>();
|
||||||
|
builder.Services.AddSingleton<GlobalComponentSvc>();
|
||||||
builder.Services.AddAuthorizationCore();
|
builder.Services.AddAuthorizationCore();
|
||||||
builder.Services.AddCascadingAuthenticationState();
|
builder.Services.AddCascadingAuthenticationState();
|
||||||
builder.Services.AddBlazoredLocalStorageAsSingleton();
|
builder.Services.AddBlazoredLocalStorageAsSingleton();
|
||||||
|
|
Loading…
Add table
Reference in a new issue