Some checks failed
Build Android APK / build-android-apk (push) Has been cancelled
Deploy Web Apps / deploy (push) Has been cancelled
Queue Release Build / prepare (push) Has been cancelled
Queue Release Build / build-linux (push) Has been cancelled
Queue Release Build / build-windows (push) Has been cancelled
Queue Release Build / finalize (push) Has been cancelled
149 lines
5.2 KiB
HTML
149 lines
5.2 KiB
HTML
<div class="relative">
|
|
@if (compact()) {
|
|
<div class="flex gap-1 rounded-lg border border-border bg-card p-2 shadow-lg">
|
|
@for (entry of shortcuts(); track entry.key) {
|
|
<button
|
|
type="button"
|
|
class="grid h-10 w-10 place-items-center rounded transition-colors hover:bg-secondary"
|
|
[attr.aria-label]="entry.label"
|
|
[title]="entry.label"
|
|
(click)="selectShortcut(entry)"
|
|
>
|
|
@if (entry.kind === 'custom') {
|
|
<img
|
|
[src]="entry.emoji.dataUrl"
|
|
[alt]="entry.emoji.name"
|
|
class="h-10 w-10 object-contain"
|
|
/>
|
|
} @else {
|
|
<span class="text-[2rem] leading-none">{{ entry.emoji }}</span>
|
|
}
|
|
</button>
|
|
}
|
|
<button
|
|
type="button"
|
|
class="grid h-10 w-10 place-items-center rounded text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground"
|
|
aria-label="{{ 'emoji.picker.openAria' | translate }}"
|
|
title="{{ 'emoji.picker.openAria' | translate }}"
|
|
(click)="openModal()"
|
|
>
|
|
<ng-icon
|
|
name="lucideSmile"
|
|
class="h-5 w-5"
|
|
/>
|
|
</button>
|
|
</div>
|
|
}
|
|
|
|
@if (!compact() || modalOpen()) {
|
|
<div
|
|
class="rounded-lg border border-border bg-card p-3 shadow-xl"
|
|
[class.absolute]="!inline()"
|
|
[class.bottom-full]="!inline()"
|
|
[class.right-0]="!inline()"
|
|
[class.z-20]="!inline()"
|
|
[class.mb-2]="!inline()"
|
|
[class.w-72]="!inline()"
|
|
[class.w-full]="inline()"
|
|
>
|
|
@if (!inline()) {
|
|
<div class="mb-2 flex items-center justify-between gap-2">
|
|
<p class="text-sm font-semibold text-foreground">{{ 'emoji.picker.title' | translate }}</p>
|
|
@if (compact()) {
|
|
<button
|
|
type="button"
|
|
class="grid h-7 w-7 place-items-center rounded hover:bg-secondary"
|
|
aria-label="Close emoji selector"
|
|
(click)="closeModal()"
|
|
>
|
|
<ng-icon
|
|
name="lucideX"
|
|
class="h-4 w-4 text-muted-foreground"
|
|
/>
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<label class="relative mb-3 block">
|
|
<ng-icon
|
|
name="lucideSearch"
|
|
class="pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground"
|
|
/>
|
|
<input
|
|
type="search"
|
|
class="w-full rounded-md border border-border bg-background py-2 pl-8 pr-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
|
placeholder="{{ 'emoji.picker.searchPlaceholder' | translate }}"
|
|
aria-label="{{ 'emoji.picker.searchAria' | translate }}"
|
|
[value]="searchQuery()"
|
|
(input)="onSearchInput($event)"
|
|
/>
|
|
</label>
|
|
|
|
<label
|
|
class="mb-3 flex cursor-pointer items-center justify-center gap-2 rounded-md border border-dashed border-border px-3 py-2 text-xs font-medium text-muted-foreground transition-colors hover:border-primary/50 hover:text-foreground"
|
|
>
|
|
<ng-icon
|
|
name="lucideUpload"
|
|
class="h-4 w-4"
|
|
/>
|
|
<span>{{ uploading() ? ('emoji.picker.uploading' | translate) : ('emoji.picker.upload' | translate) }}</span>
|
|
<input
|
|
type="file"
|
|
class="hidden"
|
|
[accept]="acceptAttribute"
|
|
[disabled]="uploading()"
|
|
(change)="uploadEmoji($event)"
|
|
/>
|
|
</label>
|
|
|
|
@if (uploadError()) {
|
|
<div class="mb-3 rounded-md border border-destructive/30 bg-destructive/10 px-2 py-1.5 text-xs text-destructive">
|
|
{{ uploadError() }}
|
|
</div>
|
|
}
|
|
|
|
@if (showEmptySearchState()) {
|
|
<div class="mb-3 rounded-md border border-border/70 bg-secondary/20 px-3 py-4 text-center text-xs text-muted-foreground">
|
|
{{ 'emoji.picker.emptySearch' | translate }}
|
|
</div>
|
|
}
|
|
|
|
@if (filteredUnicodeEntries().length > 0) {
|
|
<div class="mb-3 grid grid-cols-6 gap-1">
|
|
@for (entry of filteredUnicodeEntries(); track entry.emoji) {
|
|
<button
|
|
type="button"
|
|
class="grid h-10 w-10 place-items-center rounded transition-colors hover:bg-secondary"
|
|
(click)="selectUnicode(entry.emoji)"
|
|
>
|
|
<span class="text-[2rem] leading-none">{{ entry.emoji }}</span>
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
@if (filteredCustomEmojis().length > 0) {
|
|
<div class="grid max-h-44 grid-cols-6 gap-1 overflow-y-auto pr-1">
|
|
@for (emoji of filteredCustomEmojis(); track emoji.id) {
|
|
<button
|
|
type="button"
|
|
class="grid h-10 w-10 place-items-center rounded transition-colors hover:bg-secondary"
|
|
[title]="emoji.name"
|
|
data-custom-emoji-library
|
|
[attr.data-custom-emoji-id]="emoji.id"
|
|
(click)="selectCustom(emoji)"
|
|
>
|
|
<img
|
|
[src]="emoji.dataUrl"
|
|
[alt]="emoji.name"
|
|
class="h-10 w-10 object-contain"
|
|
/>
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|