beartropy/ui
Beartropy UI is a small Laravel UI package providing ready-made components and helpers to speed up building admin and front-end screens. Drop in common layouts, forms, and UI utilities to get a consistent look fast with minimal setup.
<x-bt-lookup />
Lookup → extends BeartropyComponent (NOT Input/InputBase)lookup.blade.php → delegates to base/input-base.blade.php for the text field + base/dropdown-base.blade.php (preset-for="select") for the dropdownresources/js/modules/lookup.js → registered as beartropyLookup via Alpine.data()input preset for colors/sizes (no separate lookup preset)resources/views/presets/sizes.phpcomponent_defaults.lookup (color only)| Prop | PHP Type | Default | Blade Attribute |
|---|---|---|---|
| id | ?string |
'beartropy-lookup-' . uniqid() |
id="my-lookup" |
| name | ?string |
same as $id |
name="country" |
| label | ?string |
null |
label="Country" |
| color | ?string |
config lookup.color ('beartropy') |
color="blue" or magic: blue |
| size | ?string |
null (resolves to 'md') |
size="lg" or magic: lg |
| placeholder | ?string |
null |
placeholder="Search..." |
| options | array |
[] |
:options="$items" |
| optionLabel | string |
'name' |
option-label="title" |
| optionValue | string |
'id' |
option-value="code" |
| value | mixed |
null |
value="pre-filled" |
| disabled | bool |
false |
:disabled="true" |
| readonly | bool |
false |
:readonly="true" |
| clearable | bool |
true |
:clearable="false" |
| iconStart | ?string |
null |
icon-start="magnifying-glass" |
| iconEnd | ?string |
null |
icon-end="chevron-down" |
| help | ?string |
null |
help="Help text" |
| hint | ?string |
null |
hint="Hint text" |
| customError | mixed |
null |
custom-error="Required" or :custom-error="$error" |
beartropy via config)primary, beartropy, red, blue, green, yellow, purple, pink, gray, orange, amber, lime, emerald, teal, cyan, sky, indigo, violet, rose, fuchsia, slate, stone, zinc, neutral
md)xs, sm, md, lg, xl
| Slot | Description |
|---|---|
| start | Content before the input field (chrome stripped by CSS) |
| end | Content after built-in controls (chrome stripped by CSS) |
The dropdown slot is used internally for the options list — not user-facing.
The constructor calls normalizeOptions() which converts all formats to [{id: string, name: string}, ...]:
| Input Format | Example | Normalized |
|---|---|---|
| Simple array | ['Apple', 'Banana'] |
[{id:'Apple', name:'Apple'}, ...] |
| Object array | [['id'=>1, 'name'=>'Foo']] |
[{id:'1', name:'Foo'}] |
| Custom keys | [['code'=>'X', 'title'=>'Y']] with option-value="code" option-label="title" |
[{id:'X', name:'Y'}] |
| Key-value pair | [['ar'=>'Argentina']] |
[{id:'ar', name:'Argentina'}] |
| Unrecognizable | [['foo'=>'bar','baz'=>'qux']] with default keys |
discarded (null) |
All id values are cast to string. Options are passed to the Alpine module via data-options JSON attribute.
beartropyLookup(cfg){ inputId, isLivewire, labelKey, valueKey, wireModelName }
open — dropdown visibilityhighlighted — index of highlighted option (-1 = none)options — full options array (synced from data-options attribute)filtered — currently filtered subsetonInput(e) — filters options, opens dropdown, syncs hidden inputmove(delta) — keyboard highlight navigation (wraps around)choose(idx) — selects option, sets visible value to label, sets hidden input to valueconfirm() — selects highlighted or syncs raw textclose() — closes dropdownclearBoth() — clears visible input + hidden Livewire inputsetVisibleValue(v) — sets visible input by ID lookup (cross-scope)$watch('$wire.' + wireModelName) (Livewire 3 pattern)data-options attribute for Livewire morphdom updates<input x-ref="livewireValue"> carries the wire:model bindingnormalize() strips combining marks via NFD + regexThe template detects Livewire mode from wire:model:
wire:model present): hidden input with wire:model, visible input shows label, Alpine syncs via $wirewire:model): visible input only, value passed via HTML value attribute, input-base manages local Alpine stateNo x-model detection (unlike Input) — Lookup always uses its own beartropyLookup Alpine module.
$errors bag using wire:model name:custom-error="$message" propfield-help componentwire:* attributes (except wire:model*)wire:loading + wire:target with auto-detected targets{{-- Basic --}}
<x-bt-lookup label="Country" placeholder="Search..." :options="['Argentina', 'Brazil', 'Canada']" />
{{-- Livewire --}}
<x-bt-lookup wire:model="country" label="Country" :options="$countries" />
{{-- Object options --}}
<x-bt-lookup
label="User"
:options="[['id' => 1, 'name' => 'John'], ['id' => 2, 'name' => 'Jane']]"
wire:model="userId" />
{{-- Custom field mapping --}}
<x-bt-lookup
:options="$cities"
option-label="title"
option-value="code"
wire:model="cityCode" />
{{-- With icon --}}
<x-bt-lookup icon-start="magnifying-glass" label="Search" :options="$items" />
{{-- Color + Size --}}
<x-bt-lookup blue sm label="Small Blue" :options="$items" />
{{-- Not clearable --}}
<x-bt-lookup :clearable="false" :options="$items" />
{{-- Disabled --}}
<x-bt-lookup :disabled="true" label="Locked" :options="$items" />
{{-- Readonly --}}
<x-bt-lookup :readonly="true" value="Fixed value" :options="$items" />
{{-- Validation error --}}
<x-bt-lookup wire:model="field" label="Required" :options="$items" />
{{-- Custom error --}}
<x-bt-lookup label="Field" custom-error="Selection required." :options="$items" />
{{-- End slot with button --}}
<x-bt-lookup label="Search" :clearable="false" :options="$items">
<x-slot:end>
<x-bt-button color="blue">Go</x-bt-button>
</x-slot:end>
</x-bt-lookup>
{{-- Start slot with icon --}}
<x-bt-lookup label="Search" :options="$items">
<x-slot:start>
<span class="flex items-center px-2 text-gray-400">
<x-bt-icon name="globe-alt" class="w-5 h-5" />
</span>
</x-slot:start>
</x-bt-lookup>
'lookup' => [
'color' => env('BEARTROPY_UI_LOOKUP_COLOR', 'beartropy'),
],
BeartropyComponent directly — NOT Input or InputBase. It has its own clean constructor with all props typed.input preset for color/size resolution (calls $getComponentPresets('input')), but default color comes from component_defaults.lookup (resolved in constructor).lookup preset file exists. The dropdown uses select preset via preset-for="select".id is auto-generated as beartropy-lookup-{uniqid} if not provided. name defaults to id.clearable defaults to true — disable with :clearable="false" when using an end slot button.help and hint are independent — both show text below the field; help takes precedence via $help ?? $hint.data-options JSON attribute. The Alpine module reads them with a MutationObserver for Livewire reactivity.document.getElementById(inputId) to access the visible input cross-scope (since input-base has its own x-data).How can I help you explore Laravel packages today?