Comment with Replies using TailwindCSS & Livewire

Tailwind CSS New Livewire

Demo

Discussion (8)

Tushar

45 minutes ago

Very straight-to-point article. Really worth time reading. Thank you! But tools are just the instruments for the UX designers. The knowledge of the design tools are as important as the creation of the design strategy.

hadi

45 minutes ago

Much appreciated! Glad you liked it ☺️

Edward Mulenga

45 minutes ago

Hope you are doing well.

Breno Silva

45 minutes ago

TALL Stack is so cool.

Tushar

45 minutes ago

This comment component is built using Livewire/Alpine and Tailwind CSS.

Mohammad

45 minutes ago

This is a comment.

hadi

45 minutes ago

This is a another comment.

This is a pro component. Get Livewiredemos pro access to view / download the component code.

Component Class


 '$refresh',
        'deleteComment' => 'confirmDelete',
        'refreshComponent' => '$refresh'
    ];

    protected $rules = [
        'newComment' => 'required|min:6',
        'editComment' => 'required|min:6',
        'replyComment' => 'required|min:6',
    ];

    public function render()
    {
        return view("livewire.pro.comment-components." . $this->view, [
            'comments' => $this->model->comments()->latest()->paginate(6),
        ]);
    }

    public function mount()
    {
        $this->user = Auth::user();
    }

    public function postComment()
    {
        if (!Auth::check())
            return;

        $this->validateOnly('newComment');

        $this->model->comment($this->newComment);

        $this->reset('newComment');

        $this->model = $this->model->refresh();
    }

    public function replyComment()
    {
        if (!Auth::check())
            return;

        $this->validateOnly('replyComment');

        $this->replyTo->comment($this->replyComment);

        $this->reset('replyComment');

        $this->emit('replyAdded');

        $this->replyTo = null;
    }

    public function selectCommentToReply(Comment $comment)
    {
        $this->replyTo = $comment;
    }

    public function cancelReply(Comment $comment)
    {
        $this->replyTo = null;
    }

    public function hydrate()
    {
        $this->resetErrorBag();
        $this->resetValidation();
    }

    public function editComment(Comment $comment)
    {
        $this->editCommentModel = $comment;
        $this->editComment = $comment->comment;
    }

    public function cancelEditing()
    {
        $this->editCommentModel = null;
    }

    public function editReply(Comment $comment)
    {
        $this->editComment = $comment->comment;
    }

    public function updateComment(Comment $comment)
    {
        if (!Auth::check())
            return;

        $this->validateOnly('editComment');

        $comment->update([
            'comment' => $this->editComment,
        ]);

        $this->reset('editComment');

        $this->model = $this->model->refresh();
    }
}

Component View File


Discussion ({{ $model->comments->count() }})

@auth
<textarea style="resize: none;" wire:model.lazy="newComment" placeholder="Write a comment..." required rows="4" class="tw-px-0 tw-w-full tw-text-sm tw-text-gray-900 tw-border-0 focus:tw-ring-0 focus:tw-outline-none @error('newComment') tw-text-red-900 tw-placeholder-red-300 focus:tw-ring-red-500 focus:tw-border-red-500 tw-border-red-300 @enderror"></textarea>
            </div>
            @error('newComment')
                <p class="tw-mt-2 tw-text-sm tw-text-red-600">{{ $message }}</p>
            @enderror
            <button type="submit"
                class="tw-inline-flex tw-items-center tw-py-2.5 tw-px-4 tw-text-xs tw-font-medium tw-text-center tw-text-white tw-bg-primary-700 tw-rounded-lg focus:tw-ring-4 focus:tw-ring-primary-200 hover:tw-bg-primary-800">
                Add comment
            </button>
        </form>
    @else
        <div class="tw-mb-6">
            <div class="tw-py-2 tw-px-4 tw-mb-4 tw-bg-white tw-rounded-lg tw-rounded-t-lg tw-border tw-border-gray-200">
                <label for="comment" class="tw-sr-only">Your comment</label>
                <textarea id="comment" style="resize: none;"
                    class="tw-px-0 tw-w-full tw-text-sm tw-text-gray-900 tw-border-0 focus:tw-ring-0 focus:tw-outline-none"
                    placeholder="Write a comment..." required disabled></textarea>
            </div>
            <button
                class="tw-inline-flex tw-items-center tw-py-2.5 tw-px-4 tw-text-xs tw-font-medium tw-text-center tw-text-white tw-bg-primary-700 tw-rounded-lg focus:tw-ring-4 focus:tw-ring-primary-200 hover:tw-bg-primary-800"
                data-bs-toggle="modal" data-bs-target="#loginModal">
                Add comment
            </button>
        </div>
    @endauth

    @foreach ($comments as $comment)
        <div>
            @if ($editCommentModel == $comment)
                <div class="tw-p-6 tw-mb-6 tw-ml-6">
                    <form class="tw-mb-6" wire:submit.prevent="updateComment({{ $comment }})">
                        <div
                            class="tw-py-2 tw-px-4 tw-mb-4 tw-bg-white tw-rounded-lg tw-rounded-t-lg tw-border tw-border-gray-200">
                            <label for="comment" class="tw-sr-only">Your comment</label>
                            <textarea style="resize: none;" wire:model.lazy="editComment" placeholder="Write a comment..."
                                class="tw-px-0 tw-w-full tw-text-sm tw-text-gray-900 tw-border-0 focus:tw-ring-0 focus:tw-outline-none @error('editComment') tw-text-red-900 tw-placeholder-red-300 focus:tw-ring-red-500 focus:tw-border-red-500 tw-border-red-300 @enderror"></textarea>
                        </div>
                        @error('editComment')
                            <p class="tw-mt-2 tw-text-sm tw-text-red-600">{{ $message }}</p>
                        @enderror
                        <button type="submit"
                            class="tw-inline-flex tw-items-center tw-py-2.5 tw-px-4 tw-text-xs tw-font-medium tw-text-center tw-text-white tw-bg-primary-700 tw-rounded-lg focus:tw-ring-4 focus:tw-ring-primary-200 hover:tw-bg-primary-800">
                            Update Comment
                        </button>
                        <button type="button" wire:click="cancelEditing()"
                            class="tw-inline-flex tw-items-center tw-py-2.5 tw-px-4 tw-text-xs tw-font-medium tw-text-center tw-border-gray-300  tw-text-gray-700 tw-bg-white hover:tw-bg-gray-50 tw-rounded-lg">
                            Cancel
                        </button>
                    </form>
                </div>
            @else
                <article class="tw-p-6 tw-mb-6 tw-ml-6 tw-text-base tw-bg-white tw-rounded-lg">
                    <footer class="tw-flex tw-justify-between tw-items-center tw-mb-2">
                        <div class="tw-flex tw-items-center">
                            <p class="tw-inline-flex tw-items-center tw-mr-3 tw-text-sm tw-text-gray-900">
                                <img class="tw-mr-2 tw-w-6 tw-h-6 tw-rounded-full"
                                    src="{{ $comment?->commentator?->avatarUrl() ?? '' }}" alt="">
                                {{ $comment?->commentator?->name ?? 'Deleted User' }}
                            </p>
                            <p class="tw-text-sm tw-text-gray-600">
                                {{ $comment->created_at->diffForHumans() }}
                            </p>
                        </div>
                    </footer>
                    <!-- Comment Body -->
                    <p class="tw-text-gray-500">
                        {{ $comment->comment }}
                    </p>
                    <!--  Reply,Edit, Delete Section -->
                    @auth
                        <div class="tw-flex tw-items-center tw-mt-4 tw-space-x-4">
                            <button type="button" wire:click="selectCommentToReply({{ $comment->id }})"
                                class="tw-flex tw-items-center tw-text-sm tw-text-gray-500 hover:tw-underline">
                                <svg aria-hidden="true" class="tw-mr-1 tw-w-4 tw-h-4" fill="none"
                                    stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                        d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z">
                                    </path>
                                </svg>
                                Reply
                            </button>
                            @if ($comment->user_id == auth()->id())
                                <button type="button" wire:click="editComment({{ $comment }})"
                                    class="tw-flex tw-items-center tw-text-sm tw-text-gray-500 hover:tw-underline">
                                    <svg class="tw-mr-1 tw-w-4 tw-h-4" xmlns="http://www.w3.org/2000/svg" fill="none"
                                        viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
                                        <path stroke-linecap="round" stroke-linejoin="round"
                                            d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
                                    </svg>
                                    Edit
                                </button>
                                <button type="button"
                                    x-on:click="window.livewire.emitTo(`pro.modals.delete-modal-component`,`showModal`,`App\\Models\\Comment`,{{ $comment->id }},`Delete Comment`,`Are you sure you want to delete this Comment?`,true)"
                                    class="tw-flex tw-items-center tw-text-sm tw-text-gray-500 hover:tw-underline">
                                    <svg class="tw-mr-1 tw-w-4 tw-h-4" xmlns="http://www.w3.org/2000/svg" fill="none"
                                        viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
                                        <path stroke-linecap="round" stroke-linejoin="round"
                                            d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
                                    </svg>
                                    Delete
                                </button>
                            @endif
                        </div>
                        @if (isset($replyTo) && $comment->id == $replyTo->id)
                            <form class="tw-mb-6 tw-mt-6 tw-ml-6" wire:submit.prevent="replyComment">
                                <div
                                    class="tw-py-2 tw-px-4 tw-mb-4 tw-bg-white tw-rounded-lg tw-rounded-t-lg tw-border tw-border-gray-200">
                                    <label for="comment" class="tw-sr-only">Your comment</label>
                                    <textarea id="comment" style="resize: none;" wire:model.lazy="replyComment" placeholder="Write a comment..."
                                        class="tw-px-0 tw-w-full tw-text-sm tw-text-gray-900 tw-border-0 focus:tw-ring-0 focus:tw-outline-none
                            @error('replyComment')
                                tw-text-red-900 tw-placeholder-red-300 focus:tw-ring-red-500 focus:tw-border-red-500 tw-border-red-300
                            @enderror"></textarea>
                                </div>
                                @error('replyComment')
                                    <p class="tw-mt-2 tw-text-sm tw-text-red-600">{{ $message }}</p>
                                @enderror
                                <button type="submit"
                                    class="tw-inline-flex tw-items-center tw-py-2.5 tw-px-4 tw-text-xs tw-font-medium tw-text-center tw-text-white tw-bg-primary-700 tw-rounded-lg focus:tw-ring-4 focus:tw-ring-primary-200 hover:tw-bg-primary-800">
                                    Reply
                                </button>
                                <button wire:click="cancelReply"
                                    class="tw-inline-flex tw-items-center tw-py-2.5 tw-px-4 tw-text-xs tw-font-medium tw-text-center tw-border-gray-300  tw-text-gray-700 tw-bg-white hover:tw-bg-gray-50 tw-rounded-lg">
                                    Cancel
                                </button>
                            </form>
                        @endif
                    @endauth
                </article>
            @endif
        </div>
        <!-- Replies -->
        @if ($comment->comments->count() > 0)
            @livewire(
                'pro.comment-components.comment-pro-tailwind',
                [
                    'model' => $comment,
                    'view' => 'comment-pro-tailwind-replies',
                ],
                key($comment->id)
            )
        @endif
    @endforeach

    {{ $comments->links() }}

</div>
@livewire('pro.modals.delete-modal-component')

Usage


@livewire('pro.comment-components.comment-pro-tailwind', [
      'model' => \App\Models\WinkPost::first(),
])