Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds date filter type #332

Merged
merged 5 commits into from
Jun 15, 2021
Merged

Adds date filter type #332

merged 5 commits into from
Jun 15, 2021

Conversation

bomshteyn
Copy link
Contributor

Adds ability to add filter of type date and pass optionally min& max options.

Here is example code:

public function filters(): array
    {
        return [
            'fromDate' => Filter::make('From Date')
                ->date([
                    'max' => now()->format('Y-m-d')
                ]),
            'toDate' => Filter::make('To Date')
                ->date([
                    'min' => isset($this->filters['fromDate']) && $this->filters['fromDate'] ? $this->filters['fromDate']:'',
                    'max' => now()->format('Y-m-d')
                ])
        ];
    }

Example Output screenshot:
Screen Shot 2021-06-08 at 9 43 26 PM

@bomshteyn
Copy link
Contributor Author

Suggested update to Creating-filters.md wiki page to match this potential addition

Creating-filters.md

@rappasoft
Copy link
Owner

I like it. I can hopefully get it in this week.

@rappasoft
Copy link
Owner

The only bug I found so far is if you change the month in the calendar, it closes the filters view. Not sure the solution to that one yet. There's another PR open to use wire:model.stop but I can't get that to work either. Let me know if you have any ideas.

@rappasoft
Copy link
Owner

We should also ensure the values match Y-m-d in the cleanFilters method just for security, I can do that if you want.

@bomshteyn
Copy link
Contributor Author

@rappasoft I will look into the closing issue. If you can add the security check for Y-m-d that would be great!

@bomshteyn
Copy link
Contributor Author

bomshteyn commented Jun 10, 2021

I see we are using @click.away in bulk actions should we switch that one also to @mousedown.away?

@rappasoft
Copy link
Owner

If it works better sure. Or maybe both?

@bomshteyn
Copy link
Contributor Author

Why both? the problem is that @click.away thinks that clicks inside a date popup are outside. Why would having @click.away & @mousedown.away any better?

@rappasoft
Copy link
Owner

It probably wouldn't, I can test later but mousedown should be fine by itself then. Thanks

@bomshteyn
Copy link
Contributor Author

We should also ensure the values match Y-m-d in the cleanFilters method just for security, I can do that if you want.

done

@rappasoft
Copy link
Owner

The calendar changing works well for tailwind, but not for bootstrap 4 or 5 because the mechanism is different. Need to find a place to stick those mousedown commands to stop the propagation for both the date and selects.

Also, I've never seen a date be handled like that, very interesting.

@rappasoft
Copy link
Owner

It's weird, sometimes it works for tailwind and sometimes it doesn't. I can't get it to work at all with bootstrap, I've tried a bunch of things and every time the filter dialog closes. Might just need to rethink this popover in general.

@rappasoft
Copy link
Owner

I can get it working 99% on the tailwind filters file by adding wire:ignore.stop to the element that x-cloak is on. It will not work for the first re-render for whatever reason. I tried implementing the same thing on bootstrap, but I'm getting alpine javascript errors and I'm not sure why yet. Might just have to implement custom popovers in bootstrap using Alpine like we did tailwind.

@bomshteyn
Copy link
Contributor Author

I didn't test with bootstrap, but with tailwind it works for me without issue. Can you describe step by step how i can replicate the issue?

@rappasoft
Copy link
Owner

Modifying anything in the filters dropdown closes the dropdown pre-maturely which is annoying and I've yet to find a fix. There's another PR right now with a different solution but I can't get that to work every time either. That's really the only thing holding this up is I know I'll get immediate issues on it.

@bomshteyn
Copy link
Contributor Author

I think we are looking into a wrong place to fix this, the issue is coming from livewire side of things. And it happens any time the filter is added or removed in the url, but not when you go from one value to a different value but maintaining the filter. Try removing wire:model from the select / date and the issue is solved.

@bomshteyn
Copy link
Contributor Author

This seems to fix it for me. it seems the closing was coming because of the filter pills element changing the dom, by appearing and disappearing. If this fix works for TW we can test is for bootstrap next.

@rappasoft
Copy link
Owner

Hmm at first glance this does seem to work for me every time.

@rappasoft
Copy link
Owner

This seems to fix it for me. it seems the closing was coming because of the filter pills element changing the dom, by appearing and disappearing. If this fix works for TW we can test is for bootstrap next.

One interesting thing I just noticed is since this includes an empty div in the dom, for Tailwind at least, the space-y-4 includes a top margin even when there are no filters. It doesn't bother me too much but we should try to get rid of it or instead of using space-y we can just conditionally apply spacing if needed.

@bomshteyn
Copy link
Contributor Author

I think it looks perfectly fine like this :-)

@rappasoft
Copy link
Owner

I'll see if I can fix that, in other news, I tried the same fix on bootstrap and it didn't work for some reason :( I'll try to figure it all out this weekend.

@rappasoft
Copy link
Owner

I have it fully working with Tailwind. Still can't get it to work with BS 4/5, I tried to swap out the dropdowns for Alpine but I'm getting some console errors which I assume are conflicts with jQuery after a quick google search. Getting there.

@bomshteyn
Copy link
Contributor Author

I have it fully working with Tailwind. Still can't get it to work with BS 4/5, I tried to swap out the dropdowns for Alpine but I'm getting some console errors which I assume are conflicts with jQuery after a quick google search. Getting there.

Do you have an existing BS4 & BS5 dev / demo repositories i can play with? Maybe i can figure this out.

@rappasoft
Copy link
Owner

rappasoft commented Jun 14, 2021

Nothing special, I just have a blank project set up with a few files.

I just swap the needed config and assets back and forth to test my test-table which just loads some dummy users and filters.

The view:

<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <title>Livewire Tables</title>

{{--        BS-4--}}
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

{{--        BS-5--}}
{{--        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">--}}

{{--        TW--}}
{{--        <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">--}}
{{--        <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>--}}

        <livewire:styles />
    </head>
    <body>
        <livewire:test-table />

        <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>

{{--        BS-4--}}
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

{{--            BS-5--}}
{{--        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>--}}

        <livewire:scripts />
    </body>
</html>

Config:

<?php

return [
//    'theme' => 'tailwind',
    'theme' => 'bootstrap-4',
//    'theme' => 'bootstrap-5',
];

@bomshteyn
Copy link
Contributor Author

The issue with using bootstrap for dropdown is that since bootstrap is modifying the dom, the livewire / alpine just replaces is again with the closed version.

One possible workaround is to add wire:ignore to the div containing the filters dropdown. The problem in that case is that you lose the ability to show filter count and only show clear inside the dropdown if the count is over 0.

I have played around with solving the above deficiency by triggering an event from updatedFilters & resetFilters something like this:

$this->emit('updateFiltersCount', count($this->getFiltersWithoutSearch()));

And then listening to it from javascript like so:

<script>
    document.addEventListener("DOMContentLoaded", () => {
        Livewire.on('updateFiltersCount', count => {
            if (count > 0) {
                $("#filter-count-badge").removeClass('d-none').text(count);
                $("#filter-clear-button").removeClass('d-none');
            } else {
                $("#filter-count-badge").addClass('d-none');
                $("#filter-clear-button").addClass('d-none');
            }
        })
    });
</script>

So with these changes plus adding wrapping div to the FilterPills as in TW version. I got it working.
But i think since we have the alpine included anyway the best way to fix this would be to use alpine uniformly across all 3 versions.

@rappasoft
Copy link
Owner

The issue with using bootstrap for dropdown is that since bootstrap is modifying the dom, the livewire / alpine just replaces is again with the closed version.

One possible workaround is to add wire:ignore to the div containing the filters dropdown. The problem in that case is that you lose the ability to show filter count and only show clear inside the dropdown if the count is over 0.

I have played around with solving the above deficiency by triggering an event from updatedFilters & resetFilters something like this:

$this->emit('updateFiltersCount', count($this->getFiltersWithoutSearch()));

And then listening to it from javascript like so:

<script>
    document.addEventListener("DOMContentLoaded", () => {
        Livewire.on('updateFiltersCount', count => {
            if (count > 0) {
                $("#filter-count-badge").removeClass('d-none').text(count);
                $("#filter-clear-button").removeClass('d-none');
            } else {
                $("#filter-count-badge").addClass('d-none');
                $("#filter-clear-button").addClass('d-none');
            }
        })
    });
</script>

So with these changes plus adding wrapping div to the FilterPills as in TW version. I got it working.
But i think since we have the alpine included anyway the best way to fix this would be to use alpine uniformly across all 3 versions.

That's good work! I did try to wire:ignore it but had issues myself.

I also replaced the bootstrap dropdown with an Alpine one, and while it worked mostly, I started getting JS errors after the first filter was changed. A quick google search pointed me to conflicts between Alpine and jQuery since that also has to be on the page.

Let me see if I can recreate it and maybe you can make more sense of the error.

@rappasoft
Copy link
Owner

rappasoft commented Jun 15, 2021

I did this quick so it probably has glaring issues.

Something like this for bootstrap-4 filters.blade.php:

@if ($showFilterDropdown && ($filtersView || count($customFilters)))
    <div
        x-data="{ open: false }"
        @keydown.escape.stop="open = false"
        @mousedown.away="open = false"
        class="btn-group d-block d-md-inline"
    >
        <button
            @click="open = !open"
            type="button"
            class="btn dropdown-toggle d-block w-100 d-md-inline"
            data-toggle="dropdown"
        >
            @lang('Filters')

            @if (count($this->getFiltersWithoutSearch()))
                <span class="badge badge-info">
                   {{ count($this->getFiltersWithoutSearch()) }}
                </span>
            @endif

            <span class="caret"></span>
        </button>
        <ul
            x-cloak
            x-show="open"
            x-transition:enter="transition ease-out duration-100"
            x-transition:enter-start="transform opacity-0 scale-95"
            x-transition:enter-end="transform opacity-100 scale-100"
            x-transition:leave="transition ease-in duration-75"
            x-transition:leave-start="transform opacity-100 scale-100"
            x-transition:leave-end="transform opacity-0 scale-95"
            class="dropdown-menu w-200"
            role="menu"
        >
            <li>
                @if ($filtersView)
                    @include($filtersView)
                @elseif (count($customFilters))
                    @foreach ($customFilters as $key => $filter)
                        <div wire:key="filter-{{ $key }}" class="p-2">
                            <label for="filter-{{ $key }}" class="mb-2">
                                {{ $filter->name() }}
                            </label>

                            @if ($filter->isSelect())
                                @include('livewire-tables::bootstrap-4.includes.filter-type-select')
                            @elseif($filter->isDate())
                                @include('livewire-tables::bootstrap-4.includes.filter-type-date')
                            @endif
                        </div>
                    @endforeach
                @endif

                @if (count($this->getFiltersWithoutSearch()))
                    <div class="dropdown-divider"></div>

                    <a
                        href="#"
                        wire:click.prevent="resetFilters"
                        class="dropdown-item"
                    >
                        @lang('Clear')
                    </a>
                @endif
            </li>
        </ul>
    </div>
@endif

It errors after selecting the first filter.

@bomshteyn
Copy link
Contributor Author

try this:

@if ($showFilterDropdown && ($filtersView || count($customFilters)))
    <div
        x-cloak
        x-data="{ open: false }"
        @keydown.escape.stop="open = false"
        @mousedown.away="open = false"
        class="btn-group d-block d-md-inline"
    >
        <button
            @click="open = !open"
            type="button"
            class="btn dropdown-toggle d-block w-100 d-md-inline"
        >
            @lang('Filters')

            @if (count($this->getFiltersWithoutSearch()))
                <span class="badge badge-info">
                   {{ count($this->getFiltersWithoutSearch()) }}
                </span>
            @endif

            <span class="caret"></span>
        </button>
        <ul
            class="dropdown-menu w-200 mt-3"
            :class="open ? 'show' : ''"
            role="menu"
        >
            <li>
                @if ($filtersView)
                    @include($filtersView)
                @elseif (count($customFilters))
                    @foreach ($customFilters as $key => $filter)
                        <div wire:key="filter-{{ $key }}" class="p-2">
                            <label for="filter-{{ $key }}" class="mb-2">
                                {{ $filter->name() }}
                            </label>

                            @if ($filter->isSelect())
                                @include('livewire-tables::bootstrap-4.includes.filter-type-select')
                            @elseif($filter->isDate())
                                @include('livewire-tables::bootstrap-4.includes.filter-type-date')
                            @endif
                        </div>
                    @endforeach
                @endif

                @if (count($this->getFiltersWithoutSearch()))
                    <div class="dropdown-divider"></div>

                    <button
                        wire:click.prevent="resetFilters"
                        class="dropdown-item btn"
                    >
                        @lang('Clear')
                    </button>
                @endif
            </li>
        </ul>
    </div>
@endif

@bomshteyn
Copy link
Contributor Author

Also make sure the header has

    <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.2/dist/alpine.min.js" defer></script>

    <style>
        [x-cloak] { display: none !important; }
    </style>

@rappasoft
Copy link
Owner

Also make sure the header has

    <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.2/dist/alpine.min.js" defer></script>

    <style>
        [x-cloak] { display: none !important; }
    </style>

Hmm due to the x-cloak on the main element the filters don't show up for me at all. Did I miss something?

@rappasoft
Copy link
Owner

Edit: I'm an idiot, I had alpine commented out. It works lovely.

@rappasoft rappasoft added the Awaiting Next Release Currently merged into development awaiting a release to master label Jun 15, 2021
@rappasoft rappasoft mentioned this pull request Jun 15, 2021
@rappasoft rappasoft merged commit d4b3c59 into rappasoft:master Jun 15, 2021
@bomshteyn bomshteyn deleted the feature/date-filter branch June 18, 2021 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Next Release Currently merged into development awaiting a release to master
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants