Skip to content

Instantly share code, notes, and snippets.

@zamith
Created April 25, 2020 15:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zamith/7016275743866c338cfff3b92391f7af to your computer and use it in GitHub Desktop.
Save zamith/7016275743866c338cfff3b92391f7af to your computer and use it in GitHub Desktop.
defmodule LocalEatsWeb.Live.Restaurants.Components.Filters do
use Surface.LiveComponent
@filters [
all: "Todos",
take_away: "Take-Away",
delivery: "Entrega em Casa"
]
property(click, :event)
property(class, :css_class)
data(active, :atom, default: :all)
def render(assigns) do
~H"""
<ul class={{ "flex", @class }}>
<li :for={{ {value, label} <- filters() }} class="mr-4">
<a href="#" :on-phx-click={{ @click }} phx-value-filter={{ value }} class={{ "text-xl px-6 py-2 rounded-full last:mr-0", colors_css(value == @active) }}>
{{ label }}
</a>
</li>
</ul>
"""
end
# Public API
def activate(id, filter) do
send_update(__MODULE__, id: id, active: filter)
end
defp filters do
@filters
end
defp colors_css(true), do: "bg-red-600 text-peach"
defp colors_css(false), do: "bg-peach text-red-600"
end
defmodule LocalEatsWeb.Live.Restaurants.Index do
use Surface.LiveView
alias LocalEats.Restaurants
alias LocalEatsWeb.Live.Restaurants.Index
alias LocalEatsWeb.Router.Helpers, as: Routes
alias LocalEatsWeb.Live.Restaurants.Components.{
RestaurantCard,
Filters,
Search
}
def render(assigns) do
~H"""
<div class="container mx-auto grid grid-cols-16 font-body my-6">
<header class="col-start-1 col-span-16 py-12 px-20 bg-peach rounded-xl text-red-600 font-display">
<div class="flex items-center">
<img class="mr-3" src="/images/logo.svg" />
<span class="text-xl">Comidaemcasa.pt</span>
</div>
<h1 class="mt-16 text-3xl">
Restaurantes perto de ti
</h1>
</header>
<div class="col-start-2 col-span-6 flex items-center mt-10">
<Search submit="search" clear="clear_search" query={{ @query }}/>
</div>
<Filters id="filters" class="col-start-2 col-span-10 mt-10 mb-6" click="update_filter" />
<p class="col-start-2 col-span-14 mt-5" :if={{Enum.empty?(@restaurants)}}>Não encontramos nada para a tua pesquisa</p>
<ul class="col-start-2 col-span-14">
<RestaurantCard
:for={{ restaurant <- @restaurants }}
restaurant={{ restaurant }}
/>
</ul>
</div>
"""
end
def mount(_params, _session, socket) do
case get_connect_params(socket) do
%{"location" => location} ->
location = {location["latitude"], location["longitude"]}
Restaurants.populate_cache(location)
restaurants = Restaurants.for(location)
{:ok,
assign(socket, restaurants: restaurants, query: nil, location: location, filter: nil)}
_ ->
{:ok, assign(socket, restaurants: [], query: nil, location: nil, filter: nil)}
end
end
def handle_event("update_filter", %{"filter" => filter}, socket) do
{:noreply,
push_patch(socket,
to: Routes.live_path(socket, Index, query: socket.assigns.query, filter: filter)
)}
end
def handle_event("search", %{"query" => query}, socket) do
{:noreply,
push_patch(socket,
to: Routes.live_path(socket, Index, query: query, filter: socket.assigns.filter)
)}
end
def handle_event("clear_search", _, socket) do
{:noreply,
push_patch(socket,
to: Routes.live_path(socket, Index, filter: socket.assigns.filter)
)}
end
def handle_params(params, _uri, socket) do
filter = String.to_existing_atom(params["filter"] || "all")
restaurants = Restaurants.for(socket.assigns.location, filter, params["query"])
Filters.activate("filters", filter)
{:noreply,
assign(socket,
restaurants: restaurants,
query: params["query"],
filter: params["filter"]
)}
end
end
defmodule LocalEatsWeb.Live.Restaurants.Components.Search do
use Surface.Component
property(submit, :event)
property(clear, :event)
property(query, :string)
def render(assigns) do
~H"""
<form :on-phx-submit={{ @submit }} id="query-form" action="#" class="flex mr-3">
<input type="hidden" name="latitude" />
<input type="hidden" name="longitude" />
<input type="text" name="query" value={{ @query }} class="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline mr-3" />
<button type="submit" class="bg-peach hover:bg-red-600 text-red-600 hover:text-peach font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">Pesquisar</button>
</form>
<a href="#" :on-phx-click={{ @clear }} :if={{ query?(assigns) }} class="text-blue-500">Limpar</a>
"""
end
defp query?(%{query: query}) when is_binary(query) do
String.length(query) > 0
end
defp query?(_), do: false
end
@msaraiva
Copy link

@zamith in order to make the formatter recognize Surface, you need to add :surface to the list of :import_deps in .formatter.exs:

[
  import_deps: [:ecto, :phoenix, :surface],
  inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
  subdirectories: ["priv/*/migrations"]
]

I believe that's why you have property(click, :event) instead of property click, :event. Right? I'll update the "Getting Started" page to add that information.

@zamith
Copy link
Author

zamith commented Apr 25, 2020

Ah, yes. I don't mind it though, so hadn't noticed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment