Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,28 @@
40% {
box-shadow: 0 2.5em 0 0;
}
}
}

/* GPU Acceleration utilities */
@layer utilities {


.contain-layout-paint {
contain: layout paint;
}

.gpu-layer {
will-change: transform;
transform: translateZ(0);
}

.smooth-scroll {
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
}

/* Otimizar imagens */
img {
content-visibility: auto; /* Lazy render */
}
33 changes: 21 additions & 12 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import {
Socket
Socket
} from "phoenix";
import {
LiveSocket
LiveSocket
} from "phoenix_live_view";
import topbar from "../vendor/topbar";
import Alpine from "alpinejs";
Expand All @@ -39,29 +39,38 @@ window.addEventListener("scroll-top", () => {
});
});

let scrollThrottle;
window.addEventListener("scroll", () => {
if (scrollThrottle) return;

scrollThrottle = setTimeout(() => {
scrollThrottle = null;
}, 100);
}, { passive: true });


let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
longPollFallbackMs: 2500,
params: {_csrf_token: csrfToken},
params: { _csrf_token: csrfToken },
dom: {
onBeforeElUpdated(from, to) {
for (const attr of from.attributes) {
if (attr.name.startsWith("data-js-")) {
to.setAttribute(attr.name, attr.value);
}
onBeforeElUpdated(from, to) {
for (const attr of from.attributes) {
if (attr.name.startsWith("data-js-")) {
to.setAttribute(attr.name, attr.value);
}
if (from._x_dataStack) {
window.Alpine.clone(from, to)
}
}
if (from._x_dataStack) {
window.Alpine.clone(from, to)
}
}
},
hooks: Hooks,
uploaders: Uploaders
})

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" })
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())

Expand Down
2 changes: 1 addition & 1 deletion lib/digistab_store_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script>
</head>
<body class="bg-white">
<body class="bg-white smooth-scroll">
<%= @inner_content %>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ defmodule DigistabStoreWeb.ProductCarousel do
this.startInterval();
}
}"
x-cloak
class="relative w-full overflow-hidden"
>
<div class="absolute inset-y-0 left-0 z-10 flex items-center">
Expand All @@ -70,8 +71,9 @@ defmodule DigistabStoreWeb.ProductCarousel do
<div
:for={{product, index} <- Enum.with_index(@featured_products)}
class={[
"absolute top-0 left-0 w-full h-full transition-transform duration-500 ease-in-out transform",
if(index == 0, do: "translate-x-0", else: "translate-x-full")
"absolute top-0 left-0 w-full h-full transition-transform duration-500
contain-layout contain-paint ease-in-out transform",
if(index == 0, do: "translate-x-0", else: "translate-x-full translate3d(100%, 0, 0)")
]}
>
<div class="grid md:grid-cols-2 gap-6 px-4">
Expand All @@ -83,6 +85,7 @@ defmodule DigistabStoreWeb.ProductCarousel do
alt={product.name}
class="w-full h-48 object-cover object-center rounded-lg transition-all duration-300 hover:scale-105"
loading="lazy"
decoding="async"
/>
<% else %>
<div class="flex items-center justify-center w-full h-48 bg-gradient-to-br from-gray-100 to-gray-200 rounded-lg animate-pulse">
Expand Down Expand Up @@ -111,6 +114,7 @@ defmodule DigistabStoreWeb.ProductCarousel do
alt={Enum.at(@featured_products, index + 1).name}
class="w-full h-48 object-cover object-center rounded-lg transition-all duration-300 hover:scale-105"
loading="lazy"
decoding="async"
/>
<% else %>
<div class="flex items-center justify-center w-full h-48 bg-gradient-to-br from-gray-100 to-gray-200 rounded-lg animate-pulse">
Expand Down
12 changes: 9 additions & 3 deletions lib/digistab_store_web/live/products/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
placeholder="search..."
value={@query}
class="w-full px-4 py-3 bg-white rounded-lg border border-gray-300 shadow-sm focus:outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500 text-gray-600 text-lg"
phx-debounce="300"
phx-debounce="500"
autocomplete="off"
readonly={@loading}
/>
Expand Down Expand Up @@ -51,8 +51,14 @@
<h2 class="text-xl font-bold mb-6"><%= @default_section_name %></h2>

<div class="container mx-auto px-4">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<.product_card :for={{_dom_id, product} <- @streams.products} product={product} />
<div
id="products-stream"
phx-update="stream"
class="grid grid-cols-1 md:grid-cols-3 gap-6 contain-layout-paint"
>
<div :for={{dom_id, product} <- @streams.products}>
<.product_card product={product} />
</div>
</div>
</div>
</section>
Expand Down
39 changes: 19 additions & 20 deletions lib/digistab_store_web/live/products/product_card_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,25 @@ defmodule DigistabStoreWeb.Components.ProductCard do
def product_card(assigns) do
~H"""
<div class="bg-white rounded-lg shadow-md overflow-hidden transition-transform hover:scale-[1.02]">
<div class="relative">
<div class="aspect-w-16 aspect-h-9">
<%= if @product.photos && Enum.any?(@product.photos) do %>
<div
phx-click={JS.patch(~p"/products/#{@product.id}") |> JS.dispatch("scroll-top")}
class="cursor-pointer"
>
<img
src={List.first(@product.photos).url}
alt={@product.name}
class="w-full h-48 object-cover object-center rounded-lg transition-all duration-300 hover:scale-105"
loading="lazy"
/>
</div>
<% else %>
<div class="flex items-center justify-center w-full h-48 bg-gradient-to-br from-gray-100 to-gray-200 rounded-lg animate-pulse">
<.icon name="hero-photo" class="w-12 h-12 text-gray-400" />
</div>
<% end %>
</div>
<div class="relative w-full aspect-[4/3] bg-gray-100">
<%= if @product.photos && Enum.any?(@product.photos) do %>
<div
phx-click={JS.patch(~p"/products/#{@product.id}") |> JS.dispatch("scroll-top")}
class="cursor-pointer"
>
<img
src={List.first(@product.photos).url}
alt={@product.name}
class="w-full h-48 object-cover object-center rounded-lg transition-all duration-300 hover:scale-105 gpu-layer"
loading="lazy"
decoding="async"
/>
</div>
<% else %>
<div class="flex items-center justify-center w-full h-48 bg-gradient-to-br from-gray-100 to-gray-200 rounded-lg animate-pulse">
<.icon name="hero-photo" class="w-12 h-12 text-gray-400" />
</div>
<% end %>
</div>
<div class="p-4 space-y-3">
<h3 class="text-lg text-gray-800 font-medium line-clamp-1">
Expand Down
Loading