yekta-kalantary/ui-livewire

Reusable Livewire components built on top of yekta-kalantary/ui.

Maintainers

Package info

github.com/yekta-kalantary/ui-livewire

pkg:composer/yekta-kalantary/ui-livewire

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main / 1.0.x-dev 2026-08-10 08:22 UTC

This package is auto-updated.

Last update: 2026-08-10 08:22:24 UTC


README

Reusable Livewire components built on top of yekta-kalantary/ui.

yekta-kalantary/ui-livewire owns reusable interactive UI state. It does not own application models, database schema, business queries, permissions or domain workflows.

Requirements

  • PHP 8.3+
  • Laravel 13
  • Livewire 4.3+
  • yekta-kalantary/ui
  • Tailwind CSS 4 when using the bundled UI styling

Installation

Both packages are available on Packagist. No custom Composer repository is required.

Before the first stable release

Both packages currently use their main development line. In a normal Laravel application whose minimum-stability is stable, require both development packages explicitly at the root:

composer require \
    yekta-kalantary/ui:dev-main \
    yekta-kalantary/ui-livewire:dev-main

Both main branches are aliased to 1.0.x-dev for Composer version resolution.

After stable 1.x tags exist

Installation will become:

composer require yekta-kalantary/ui-livewire

Composer will then resolve a compatible stable yekta-kalantary/ui automatically.

Laravel package discovery registers Yekta\UiLivewire\UiLivewireServiceProvider automatically.

Tailwind CSS 4 setup

The interactive components render Blade from both packages. Scan both vendor view directories and import the base UI stylesheet:

@import 'tailwindcss';
@import '../../vendor/yekta-kalantary/ui/resources/css/ui.css';

@source '../../vendor/yekta-kalantary/ui/resources/views/**/*.blade.php';
@source '../../vendor/yekta-kalantary/ui-livewire/resources/views/**/*.blade.php';

Without both @source entries, Tailwind may omit package utility classes from the final application CSS.

SearchSelect

The first interactive component is a generic server-backed search select.

It separates reusable interaction behavior from application-owned search logic:

SearchSelect
    ↓
SearchProvider contract
    ↓
Application query / service

The package owns:

  • debounced Livewire search state;
  • minimum-character behavior;
  • dropdown visibility;
  • loading and no-result states;
  • selected-value synchronization;
  • clearing;
  • disabled state;
  • result limits;
  • selected-value validation through the provider.

The consuming application owns:

  • which model or service is searched;
  • which database columns are searched;
  • authorization and tenant scope;
  • result labels and descriptions;
  • indexes and query performance.

Quick start

Create a provider:

<?php

declare(strict_types=1);

namespace App\Ui;

use App\Models\Contact;
use Yekta\UiLivewire\Contracts\SearchProvider;
use Yekta\UiLivewire\Support\SearchOption;

final class ContactSearchProvider implements SearchProvider
{
    public function search(string $query, int $limit): array
    {
        return Contact::query()
            ->where(function ($builder) use ($query) {
                $builder
                    ->where('first_name', 'like', "%{$query}%")
                    ->orWhere('last_name', 'like', "%{$query}%")
                    ->orWhere('mobile', 'like', "%{$query}%")
                    ->orWhere('email', 'like', "%{$query}%");
            })
            ->limit($limit)
            ->get()
            ->map(fn (Contact $contact) => new SearchOption(
                value: $contact->getKey(),
                label: trim($contact->first_name.' '.$contact->last_name),
                description: $contact->mobile ?: $contact->email,
            ))
            ->all();
    }

    public function find(string|int $value): ?SearchOption
    {
        $contact = Contact::query()->find($value);

        if (! $contact) {
            return null;
        }

        return new SearchOption(
            value: $contact->getKey(),
            label: trim($contact->first_name.' '.$contact->last_name),
            description: $contact->mobile ?: $contact->email,
        );
    }
}

Use it from a parent Livewire view:

<livewire:ui-livewire::search-select
    wire:model="contactId"
    :provider="\App\Ui\ContactSearchProvider::class"
    label="مخاطب"
    placeholder="نام، موبایل یا ایمیل..."
/>

Parent component property:

public int|string|null $contactId = null;

For the full lifecycle, props, edge cases and examples, see SearchSelect reference.

SearchProvider

Every search implementation must implement:

use Yekta\UiLivewire\Contracts\SearchProvider;
use Yekta\UiLivewire\Support\SearchOption;

interface SearchProvider
{
    /** @return list<SearchOption> */
    public function search(string $query, int $limit): array;

    public function find(string|int $value): ?SearchOption;
}

Providers are resolved through Laravel's service container, so application services can be injected into their constructors.

find() is not optional. It is the trust boundary used to validate and hydrate selected values.

See Provider design for query, authorization, tenancy and performance guidance.

SearchOption

A result is represented by an immutable value object:

use Yekta\UiLivewire\Support\SearchOption;

$option = new SearchOption(
    value: 15,
    label: 'Yekta Kalantary',
    description: '09121234567',
);
Property Type Required
value `string int`
label string yes
description ?string no

Localization

English and Persian translations are included for built-in component states:

  • placeholder
  • clear action
  • searching state
  • minimum-character feedback
  • no-results state

The active Laravel locale is used automatically.

Publish overrides with:

php artisan vendor:publish --tag=ui-livewire-translations

Destination:

lang/vendor/ui-livewire

See Styling and localization.

Publishing views

Publishing is optional:

php artisan vendor:publish --tag=ui-livewire-views

Destination:

resources/views/vendor/ui-livewire

Prefer the vendor view unless an application intentionally needs a markup override.

Architecture

Dependency direction is one-way:

ui-livewire
    ↓
   ui

ui-livewire may depend on ui. The base package must never depend on Livewire or ui-livewire.

Business-specific components stay in the application unless their behavior can be expressed through a generic contract.

Examples that should stay application-owned:

  • ContactSearchProvider
  • TicketAssigneeSearchProvider
  • permission checks
  • tenant scoping
  • Eloquent query details

The reusable SearchSelect interaction belongs here.

Development

git clone https://github.com/yekta-kalantary/ui-livewire.git
cd ui-livewire
composer update
composer test

Validate Composer metadata:

composer validate --strict

CI covers:

  • PHP 8.3 and 8.4;
  • lowest supported dependency versions;
  • highest matching dependency versions;
  • package registration and localization;
  • SearchSelect state transitions and provider validation.

See Architecture, testing and development.

Documentation

License

MIT