Skip to main content

Actions

Reactions code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/reactions.blade.php

@props([
    'uuid' => null,
    'uuidKey' => null,
])

@php
    /*
    | Emoji reaction pills with picker. Must live inside `<x-ui.comments>` so the
    | parent Alpine scope exposes `reactionStates`, `toggleReaction`, and
    | `togglePicker`.
    |
    | Pass `uuid` for server-rendered comments, or `uuidKey` for Alpine scope
    | variables such as `comment.uuid` or `reply.uuid`.
    |
    | Usage:
    |   <x-ui.reactions :uuid="$comment->uuid" />
    |   <x-ui.reactions uuid-key="comment.uuid" />
    */
    $emojis = \App\ReactionEmoji::values();
    $resolvedKey = $uuidKey ?? "'{$uuid}'";
@endphp

<div {{ $attributes->merge(['class' => 'mt-2 flex flex-wrap items-center gap-1.5']) }}>
    <template x-for="reaction in (reactionStates[{{ $resolvedKey }}] || [])" :key="reaction.emoji">
        <button
            type="button"
            @click="toggleReaction({{ $resolvedKey }}, reaction.emoji)"
            :class="reaction.reacted
                ? 'border-brand-200 bg-brand-50 text-brand-700 dark:border-brand-800 dark:bg-brand-950/40 dark:text-brand-300'
                : 'border-gray-200 bg-gray-50 text-gray-600 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-300'"
            class="inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium transition hover:border-brand-300 dark:hover:border-brand-700"
            :aria-pressed="reaction.reacted.toString()"
        >
            <span x-text="reaction.emoji" aria-hidden="true"></span>
            <span x-text="reaction.count"></span>
        </button>
    </template>

    <div class="relative">
        <button
            type="button"
            @click="togglePicker({{ $resolvedKey }})"
            class="inline-flex h-7 w-7 items-center justify-center rounded-full border border-gray-200 bg-gray-50 text-gray-500 transition hover:border-brand-300 hover:text-brand-600 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400 dark:hover:border-brand-700 dark:hover:text-brand-400"
            aria-label="Add reaction"
        >
            <x-ui.icon name="smiley" weight="bold" size="sm" />
        </button>

        <div
            x-show="pickerOpenFor === {{ $resolvedKey }}"
            x-cloak
            @click.outside="pickerOpenFor = null"
            x-transition:enter="transition ease-out duration-100"
            x-transition:enter-start="opacity-0 scale-95"
            x-transition:enter-end="opacity-100 scale-100"
            x-transition:leave="transition ease-in duration-75"
            x-transition:leave-start="opacity-100 scale-100"
            x-transition:leave-end="opacity-0 scale-95"
            class="absolute z-10 mt-1 flex gap-0.5 rounded-xl border border-gray-200 bg-white p-1 shadow-lg ring-1 ring-black/5 dark:border-gray-800 dark:bg-gray-900"
        >
            @foreach ($emojis as $emoji)
                <button
                    type="button"
                    @click="toggleReaction({{ $resolvedKey }}, @js($emoji)); pickerOpenFor = null"
                    class="flex h-8 w-8 items-center justify-center rounded-lg text-base transition hover:bg-gray-100 dark:hover:bg-gray-800"
                    aria-label="React with {{ $emoji }}"
                >{{ $emoji }}</button>
            @endforeach
        </div>
    </div>
</div>