Laravel and Livewire change button style on click - laravel

I have two buttons in tailwindcss in my
<div class="flex flex-row">
<button class="-ml-px relative inline-flex items-center space-x-2 px-4 py-4 border border-blue-300 text-sm font-medium rounded-l-md text-white bg-blue-500 hover:bg-blue-600 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-blue-500">
<span>Add</span>
</button>
<button class="-ml-px relative inline-flex items-center space-x-2 px-4 py-4 border border-gray-300 text-sm font-medium text-gray-900 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-blue-500">
<span>Set</span>
</button>
</div>
one button is blue and other is grey.
I want to switch colour when I click on Set Button and also same when I click back to Add button again.

As I mentioned, Livewire is for interaction with backend code. If you want to style frontend elements following frontend interaction, use a JS Framework like AlpineJS or plain CSS.
If you really just want to change focus color, you can go with a variation of #Digvijay's answer:
<div class="flex space-x-4">
<button class="flex px-4 py-2 bg-gray-100 text-gray-900 cursor-pointer hover:bg-blue-200 focus:text-blue-700 focus:bg-blue-200 focus:outline-none focus:ring-blue-600" tabindex="1">Add</button>
<button class="flex px-4 py-2 bg-gray-100 text-gray-900 cursor-pointer hover:bg-blue-200 focus:text-blue-700 focus:bg-blue-200 focus:outline-none focus:ring-gray-600" tabindex="2">Set</button>
</div>
(see https://play.tailwindcss.com/mwspfpsTuU)
If you want the colors to stick even after the focus is lost, you may use something like this with AlpineJS:
<div x-data="{ highlightedButton: '' }" class="flex space-x-4">
<button #click="highlightedButton='add'" class="flex px-4 py-2 bg-gray-100 text-gray-900 hover:bg-blue-200" :class="{'bg-blue-400': highlightedButton === 'add'}" tabindex="1">Add</button>
<button #click="highlightedButton='set'" class="flex px-4 py-2 bg-gray-100 text-gray-900 hover:bg-blue-200" :class="{'bg-blue-400': highlightedButton === 'set'}" tabindex="2">Set</button>
</div>
And finally if you keep track of the Add / Set state in the Livewire component anyways (which is hard to tell because there is no livewire code in your markup at all), then do it like #AliAli described in his answer. :-)

Edit:
If anyone came to this answer, this is a solution when storing the button state is needed, in other cases, you should follow #Lupinity Labs answer.
You can use Livewire event listeners to change the frontend.
First, declare variables inside the Livewire PHP model like:
public $isSetClicked = false;
public $isAddClicked = false;
Then decalre functions to handle the onClick event:
public function SetClicked()
{
//this will give you toggling behavior
$this->isSetClicked == false ? $this->isSetClicked = true : $this->isSetClicked = false;
}
and declare the AddClicked the same way.
Then inside your Livewire template add the events to the buttons like:
<button wire:click="SetClicked()" class="{{$isSetClicked ? "color-class" : "other-color-class"}} the rest of your css classes">

To toggle HTML attributes with livewire, for example class, id, href and so on you have to work with AlpineJS inside livewire (docs). For example you want to change some element CSS class attribute by changing a some internal state (model) inside your livewire component. For example by clicking a some link.
/app/Http/Livewire/YourLivewireComponent.php
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class YourLivewireComponent extends Component
{
public $classChanged = false;
public function render()
{
return view('livewire.your-liveweire-component');
}
public function myClickFunction()
{
$this->classChanged = true;
}
}
/resources/views/livewire/your-livewire-component.blade.php
Click me
<div x-data="{ alpine_class_changed: #entangle('classChanged') }" class="some classes" x-bind:class="alpine_class_changed ? 'my_add_class' : ''">some text</div>
/resources/views/somelaraveltemplate.blade.php
<p>some HTML code</p>
#livewire('your-liveweire-component')

You will need to use tabindex. Checkout focus change on button working example.
<div class="flex space-x-4">
<div class="flex px-4 py-2 bg-blue-600 text-blue-100 cursor-pointer hover:bg-blue-700 focus:text-blue-700 focus:bg-blue-200 focus:outline-none focus:ring-blue-600" tabindex="1">Add</div>
<div class="flex px-4 py-2 bg-gray-100 text-gray-600 cursor-pointer hover:bg-gray-200 focus:text-gray-100 focus:bg-gray-600 focus:outline-none focus:ring-gray-600" tabindex="1">Set</div>
</div>

Related

how to add onClick when conditional rendering for profile dropdown

I am trying to add the conditional rendering for user dropdown, so once the user is signed in, and when they click the account icon, the page will show the dropdown menu of that user. i am using useSelector to track the user state, right now, the dropdown menu is shown under the icon once the user sign in.... i want to add the onclick functionality on that, so they need to click the account icon, and then the menu will pop up, anyone know how to add that?
export default function Example() {
const user = useSelector((state) => state.user);
return (
<div className="bg-white">
<div className="ml-auto flex items-center">
<div className="hidden relative lg:flex lg:flex-1 lg:items-center lg:justify-end lg:space-x-6">
<UserCircleIcon
className="h-6 w-6 flex-shrink-0 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
{user && (
<div className="w-24 z-10 rounded-md bg-white py-1 shadow-lg flex flex-col absolute mt-36 right-0 ring-1 ring-black ring-opacity-5 focus:outline-none">
<p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 trainstion-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700">
Profile
</p>
<p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 trainstion-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700">
Settings
</p>
<p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 trainstion-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700">
Signout
</p>
</div>
)}
</div>
If I understand your question correctly, what you need an additional local state to keep track of whether the user clicked on the "user" icon. You will need to pass that extra condition to your conditional rendering write a function to change the state to true once the user click on the user icon.
export default function Example() {
const user = useSelector((state) => state.user);
const [isOpen, setIsOpen] = useState(false); <--- new local state/condition ---<<
const handleOpenUserMenu = () => setIsOpen(true); <--- set function ---<<
return (
<div className="bg-white">
<div className="ml-auto flex items-center">
<div className="hidden relative lg:flex lg:flex-1 lg:items-center lg:justify-end lg:space-x-6">
<UserCircleIcon
className="h-6 w-6 flex-shrink-0 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
onClick={handleOpenUserMenu} <--- pass the set function here ---<<
/>
{user && isOpen && ( <--- add the extra condition here ---<<
<div className="w-24 z-10 rounded-md bg-white py-1 shadow-lg flex flex-col absolute mt-36 right-0 ring-1 ring-black ring-opacity-5 focus:outline-none">
<p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 trainstion-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700">
Profile
</p>
<p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 trainstion-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700">
Settings
</p>
<p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 trainstion-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700">
Signout
</p>
</div>
)}
</div>
</div>
</div>
);
}

nest and previous pagination inside div

So, I have created a Laravel controller with pagination. The code inside my Vue file is the following
<div class="flex justify-between flex-wrap">
<div v-for="kink in kinks.data" :key="kink.id" :value="kink.name">
<button type="button" class="m-1 my-2 inline-flex items-center px-3.5 py-2 border border-transparent text-sm leading-4 font-medium rounded-full shadow-sm text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"> {{ kink.name }}</button>
</div>
</div>
<div class="flex justify-between">
<ArrowLeftIcon type="button" :href="kinks.next_page_url" class="m-2 inline-flex items-center px-2 py-1 h-7 rounded-full border border-transparent text-sm leading-4 font-medium shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 "></ArrowLeftIcon>
<ArrowRightIcon type="button" :href="kinks.last_page_url" class="m-2 inline-flex items-center px-2 py-1 h-7 rounded-full border border-transparent text-sm leading-4 font-medium shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"></ArrowRightIcon>
</div>
The arrow buttons should change the v-for data based on the active page. I have NEVER done this before and I do not want to use some cheap npm package when I just need next and previous.
IDEALLY, if someone is on page 1, and they click previous, it should go to the last page and the same with last page next should go to page 1
How do you implement this?
The data object inside vue from the pagination provides the following:
kinks:Object
current_page:1
data:Array[13]
first_page_url:"http://localhost:8000/kinks?page=1"
from:1
last_page:5
last_page_url:"http://localhost:8000/kinks?page=5"
links:Array[7]
next_page_url:"http://localhost:8000/kinks?page=2"
path:"http://localhost:8000/kinks"
per_page:13
prev_page_url:null
to:13
total:63
ok so you will want to change the way your buttons work then. e.g. add a #click method to them instead of a href
example
<button #click="previousLink">
...icon
</button>
<script>
export default {
methods: {
previousLink() {
if (this.prev_page_url === null) {
window.location.href = this.last_page_url;
} else {
window.location.href = this.prev_page_url;
}
}
}
}
</script>

Bind Livewire component to parent array

I have the following structure:
A parent component:
class SiteEditComponent extends Component
{
protected $listeners = ['upClicked', 'downClicked', 'childUpdated'];
public $childBlocks = [];
public function render()
{
return view('livewire.site-edit-component', ['childBlocks' => $this->childBlocks]);
}
}
Which renders that way:
<div>
<button wire:click="addBlock" type="button" class=" mb-3 inline-flex items-center px-2.5 py-1.5 border border-gray-300 shadow-sm text-xs font-medium rounded text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Block hinzufügen</button>
#foreach($childBlocks as $key => $childBlockContent)
<livewire:templates.text-block-component wire:model.debounce="childBlocks.key" :block="$childBlockContent" :wire:key="$key">
#endforeach
</div>
And the child component(s):
class TextBlockComponent extends Component
{
public $block;
protected $rules = [
'block.title' => ''
];
public function render()
{
return view('livewire.text-block-component', [
'block' => $this->block
]);
}
Which renders that way (simplified):
<div>
<div class="bg-white shadow sm:rounded-lg mb-3">
<div class="px-4 py-5 sm:p-6">
<div
class=" w-1/3 border border-gray-300 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-indigo-600 focus-within:border-indigo-600">
<label for="name" class="block text-xs font-medium text-gray-900">Titel</label>
<input wire:model.debounce="block.title" type="text" name="title" id="name"
class="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder="Blocküberschrift">
</div>
</div>
</div>
</div>
In fact both components represent a Laravel model. A site can have multiple blocks.
What would be the right way to keep track of the child components in the parent component?
I want to implement a save button which should collect all items of $childBlocks, save the Site and attach the childBlocks to it. I tried to model it by using wire:model.debounce="childBlocks.key" but the binding doesn't seem to work.
I wanted to create an associative array in which every block is identified by a randomly generated key which holds the block data.

Avoid mutating a prop directly for #click

I'm trying to import and use the JetButton component from laravel jetstream. I've added the #click emit event to the jetbutton but getting the error:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.
Component
<template>
<jet-button #click="update()">
Update
</jet-button>
</template>
<script>
method:{
update(){ //doing something }
}
</script>
JetButton
<template>
<button #click="$emit('click', $event);" :type="type" class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:shadow-outline-gray transition ease-in-out duration-150">
<slot></slot>
</button>
</template>
When emitting an event you need to define the method first which will be called and then emit that event from the method.
The error tells that, don't emit event directly in the prop.
On #click call a method defined in the JetButton component and then use this.$emit('call').
Component
<template>
<jet-button #call="update">
Update
</jet-button>
</template>
<script>
methods () {
update(){ //doing something }
}
</script>
JetButton
<template>
<button #click="call" :type="type" class="inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:shadow-outline-gray transition ease-in-out duration-150">
<slot></slot>
</button>
</template>
<script>
methods () {
call () {
this.$emit('call')
}
}
</script>
Handle the event call in the component as <jet-button #call="update"> and then define that method in the methods() section.

Laravel live wire accordion collapsing on input change

I am using accordion something like this
https://www.tailwindtoolbox.com/components/accordion and I have a input field inside my accordion
<div class="tab-content overflow-y-scroll border-l-2 bg-gray-100 border-indigo-500 leading-normal">
<div class="border border-black mt-3 p-3 grid grid-cols-4">
<div class="col-span-1">
<label for="about" class="block text-sm leading-5 font-medium text-gray-700">
Title
</label>
<div class="rounded-md shadow-sm">
<textarea wire:model="additional_docs.title" name="additional_docs" id="edit_additional_docs" name="" rows="3" class="form-textarea mt-1 block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5" placeholder="description"></textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Document Title
</p>
</div>
</div>
The accordion is working fine but as soon as I start typing the accordion closes and I need to open accordion again and start typing, but if I remove wire:model ,it works fine .I am new to live wire and if I use wire:ignore it doesn't help too.
Thanks for any help :)
Add wire:ignore.self on the element which will be hidden/shown.

Resources