3

I have the following blade component in laravel. I am using tailwind. located in views/components/indigo-button-sm.blade.php

<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex justify-center py-1 px-1 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500']) }}>
    {{ $slot }}
</button>

I want to be able to change the component on the fly a bit. That is, i use the above component to show a "save" button. But then I want to show a button that says "confirm" your entry and change the look of the button slightly to hint visually that this is a different choice.

specifically, i have an autocomplete and the user is entering in an item that I am providing over 2,000 choices (schools). But if the user types in a school that is not in my database table list, I want them to confirm that they typed their school in correctly, so i want to show the "Confirm" your entry button, just to make sure. If they choose a school that is already in the list, I will not ask them to confirm their typing.

So, I want to change the save button

bg-indigo-600 hover:bg-indigo-700

to the Confirm button with a slightly different color scheme.

bg-indigo-800 hover:bg-indigo-900

or

<x-indigo-button-sm x-show="showsaveschool" wire:click="saveSchoolInfo">Save</x-indigo-button-sm>
<x-indigo-button-sm x-show="showconfirmschool" wire:click="confirmSaveSchoolInfo">Confirm</x-indigo-button-sm>

so they type in the school and the school is not in the lookup list. They click the Save button. The save button disappears and the confirm button shows and a piece of text will show up just below the confirm button.

As I type this, I am beginning to think it will be better to just create a Confirm button and always use it.

any way. I thought $attributes(merge['']) would allow you to change attributes, but I think it only allow syou to add attributes. So if you have a margin class, say "my-0" in the component, it doesn't seem to allow for a new similar class such as "my-5"

Your comments would be appreaciated.

miken32
  • 42,008
  • 16
  • 111
  • 154
  • 1
    Merging does exactly what it says. If you specify a `class` attribute when you call the component (e.g. ``) the values will be merged together. – miken32 Dec 03 '21 at 23:06
  • i am often wrong but never in doubt, but it seems that if m-2 is used in the component, and you pass a class of m-10 in the html, that m-2 will not be merged into m-10. it stays at m-2. But if p-5 is not in the component definition, and you pass in p-5, the p-5 will be merged into the component. Yes? – Robert Bryan Davis Dec 04 '21 at 01:16
  • 1
    No, m-2 and m-10 are 2 different strings, so they will both be in the merged value of the class attribute. – miken32 Dec 04 '21 at 18:28
  • yes. but which one will "win". The first one, yes? And the one is in the component will be the winner will it not, because it will come first if the second one is added in the html.? – Robert Bryan Davis Dec 04 '21 at 18:59
  • 1
    If you're asking about CSS cascading rules, that is a whole different topic. Assuming equal specificity, the rule that comes last in the stylesheet will take precedence. The order of elements in the HTML class attribute is not relevant. See [here](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance#understanding_the_cascade) and [here](https://stackoverflow.com/a/1321722/1255289) – miken32 Dec 04 '21 at 19:04
  • so if the compnent you have written has py-1 class in the $attributes->merge(['']), and you want to tweek the padding a bit and nothing else to py-5, and py-1 comes last in the css, the py-1 will take precedence, correct? how would the original button component above be re-written to allow for py-5 to take precedence so i could use the component like this go Props correct? how would you write the component so you could make the 'py-5' take precedence assuming that the 'py-1' in the definition will take precedence because it comes last in CSS? – Robert Bryan Davis Dec 04 '21 at 19:21
  • 1
    Conditional merge: https://laravel.com/docs/8.x/blade#conditionally-merge-classes – miken32 Dec 04 '21 at 19:28
  • thank you for sticking with me on this. I appreciate your time. – Robert Bryan Davis Dec 04 '21 at 19:35

1 Answers1

6

For the class attribute, you can use conditional merging to ensure that you only insert the attribute if a conflicting one hasn't been set. The class() method is passed an array. If the array entry has a numeric index, the value is always merged into the class attribute. But if the entry has a non-numeric index, the index is merged into the class attribute if the value is true.

<button
    {{ $attributes->class([
          'inline-flex justify-center py-1 px-1',
          'border border-transparent shadow-sm',
          'text-sm font-medium rounded-md text-white',
          'focus:outline-none focus:ring-2',
          'focus:ring-offset-2 focus:ring-indigo-500'
          'bg-indigo-600 hover:bg-indigo-700' => hasCustomBackground(),
       ])
       ->merge([
          'type' => 'button',
       ])
    }}
>
    {{ $slot }}
</button>

Then, create a hasCustomBackground() component method based on the passed class value:

class IndigoButtonSm extends Component
{
    public string class;

    /**
     * Checks if the class attribute contains "bg-indigo"
     */
    public function hasCustomBackground(): bool
    {
        $classes = collect(explode(" ", $this->class));
        return $classes->contains(fn ($c) => str_contains("bg-indigo", $c));
    }

    ...
}

I've never used this functionality before, but based on the documentation it should work?

miken32
  • 42,008
  • 16
  • 111
  • 154