2026-02-18 21:23:15 +00:00
|
|
|
|
defmodule BerrypodWeb.ShopComponents.Layout do
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
use Phoenix.Component
|
|
|
|
|
|
|
2026-02-18 21:23:15 +00:00
|
|
|
|
import BerrypodWeb.ShopComponents.Cart
|
|
|
|
|
|
import BerrypodWeb.ShopComponents.Content
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the announcement bar.
|
|
|
|
|
|
|
|
|
|
|
|
The bar displays promotional messaging at the top of the page.
|
|
|
|
|
|
It uses CSS custom properties for theming.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `theme_settings` - Required. The theme settings map.
|
|
|
|
|
|
* `message` - Optional. The announcement message to display.
|
|
|
|
|
|
Defaults to "Free delivery on orders over £40".
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.announcement_bar theme_settings={@theme_settings} />
|
|
|
|
|
|
<.announcement_bar theme_settings={@theme_settings} message="20% off this weekend!" />
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :theme_settings, :map, required: true
|
|
|
|
|
|
attr :message, :string, default: "Sample announcement – e.g. free delivery, sales, or new drops"
|
|
|
|
|
|
|
|
|
|
|
|
def announcement_bar(assigns) do
|
|
|
|
|
|
~H"""
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="announcement-bar">
|
|
|
|
|
|
<p>{@message}</p>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the skip link for keyboard navigation accessibility.
|
|
|
|
|
|
|
|
|
|
|
|
This is a standard accessibility pattern that allows keyboard users
|
|
|
|
|
|
to skip directly to the main content.
|
|
|
|
|
|
"""
|
|
|
|
|
|
def skip_link(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<a href="#main-content" class="skip-link">
|
|
|
|
|
|
Skip to main content
|
|
|
|
|
|
</a>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2026-02-13 16:02:25 +00:00
|
|
|
|
# Keys accepted by shop_layout — used by layout_assigns/1 so page templates
|
|
|
|
|
|
# can spread assigns without listing each one explicitly.
|
2026-03-09 09:01:21 +00:00
|
|
|
|
@layout_keys ~w(theme_settings generated_css site_name logo_image header_image mode cart_items cart_count
|
2026-02-14 10:48:00 +00:00
|
|
|
|
cart_subtotal cart_total cart_drawer_open cart_status active_page error_page is_admin
|
|
|
|
|
|
search_query search_results search_open categories shipping_estimate
|
2026-03-09 09:01:21 +00:00
|
|
|
|
country_code available_countries editing theme_editing editor_current_path editor_sidebar_open
|
|
|
|
|
|
editor_active_tab editor_sheet_state editor_dirty editor_save_status
|
2026-03-04 14:02:49 +00:00
|
|
|
|
header_nav_items footer_nav_items newsletter_enabled newsletter_state stripe_connected)a
|
2026-02-13 16:02:25 +00:00
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Extracts the assigns relevant to `shop_layout` from a full assigns map.
|
|
|
|
|
|
|
|
|
|
|
|
Page templates can use this instead of listing every attr explicitly:
|
|
|
|
|
|
|
|
|
|
|
|
<.shop_layout {layout_assigns(assigns)} active_page="home">
|
|
|
|
|
|
...
|
|
|
|
|
|
</.shop_layout>
|
|
|
|
|
|
"""
|
|
|
|
|
|
def layout_assigns(assigns) do
|
|
|
|
|
|
Map.take(assigns, @layout_keys)
|
|
|
|
|
|
end
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
@doc """
|
|
|
|
|
|
Wraps page content in the standard shop shell: container, header, footer,
|
|
|
|
|
|
cart drawer, search modal, and mobile bottom nav.
|
|
|
|
|
|
|
|
|
|
|
|
Templates pass their unique `<main>` content as the inner block.
|
|
|
|
|
|
The `error_page` flag disables the CartPersist hook and mobile bottom nav.
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :theme_settings, :map, required: true
|
2026-03-03 14:52:31 +00:00
|
|
|
|
attr :site_name, :string, required: true
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :logo_image, :any, required: true
|
|
|
|
|
|
attr :header_image, :any, required: true
|
|
|
|
|
|
attr :mode, :atom, required: true
|
|
|
|
|
|
attr :cart_items, :list, required: true
|
|
|
|
|
|
attr :cart_count, :integer, required: true
|
|
|
|
|
|
attr :cart_subtotal, :string, required: true
|
2026-02-14 10:48:00 +00:00
|
|
|
|
attr :cart_total, :string, default: nil
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :cart_drawer_open, :boolean, default: false
|
|
|
|
|
|
attr :cart_status, :string, default: nil
|
|
|
|
|
|
attr :active_page, :string, required: true
|
|
|
|
|
|
attr :error_page, :boolean, default: false
|
2026-02-13 16:02:25 +00:00
|
|
|
|
attr :is_admin, :boolean, default: false
|
2026-02-27 16:22:35 +00:00
|
|
|
|
attr :editing, :boolean, default: false
|
|
|
|
|
|
attr :editor_current_path, :string, default: nil
|
|
|
|
|
|
attr :editor_sidebar_open, :boolean, default: true
|
2026-02-13 07:29:19 +00:00
|
|
|
|
attr :search_query, :string, default: ""
|
|
|
|
|
|
attr :search_results, :list, default: []
|
2026-02-13 16:02:25 +00:00
|
|
|
|
attr :search_open, :boolean, default: false
|
2026-02-14 10:48:00 +00:00
|
|
|
|
attr :shipping_estimate, :integer, default: nil
|
|
|
|
|
|
attr :country_code, :string, default: "GB"
|
|
|
|
|
|
attr :available_countries, :list, default: []
|
2026-02-28 11:18:37 +00:00
|
|
|
|
attr :header_nav_items, :list, default: []
|
|
|
|
|
|
attr :footer_nav_items, :list, default: []
|
2026-02-28 23:25:28 +00:00
|
|
|
|
attr :newsletter_enabled, :boolean, default: false
|
|
|
|
|
|
attr :newsletter_state, :atom, default: :idle
|
2026-03-04 14:02:49 +00:00
|
|
|
|
attr :stripe_connected, :boolean, default: true
|
2026-03-09 09:01:21 +00:00
|
|
|
|
attr :generated_css, :string, default: nil
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
slot :inner_block, required: true
|
|
|
|
|
|
|
|
|
|
|
|
def shop_layout(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<div
|
|
|
|
|
|
id={unless @error_page, do: "shop-container"}
|
|
|
|
|
|
phx-hook={unless @error_page, do: "CartPersist"}
|
2026-03-09 09:01:21 +00:00
|
|
|
|
class="shop-container themed"
|
2026-02-17 09:03:35 +00:00
|
|
|
|
data-bottom-nav={!@error_page || nil}
|
2026-03-09 09:01:21 +00:00
|
|
|
|
data-mood={@theme_settings.mood}
|
|
|
|
|
|
data-typography={@theme_settings.typography}
|
|
|
|
|
|
data-shape={@theme_settings.shape}
|
|
|
|
|
|
data-density={@theme_settings.density}
|
|
|
|
|
|
data-grid={@theme_settings.grid_columns}
|
|
|
|
|
|
data-header={@theme_settings.header_layout}
|
|
|
|
|
|
data-sticky={to_string(@theme_settings.sticky_header)}
|
|
|
|
|
|
data-layout={@theme_settings.layout_width}
|
|
|
|
|
|
data-shadow={@theme_settings.card_shadow}
|
|
|
|
|
|
data-button-style={@theme_settings.button_style}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
2026-03-09 09:01:21 +00:00
|
|
|
|
<%!-- Live-updatable theme CSS (overrides static version in head) --%>
|
|
|
|
|
|
<%= if @generated_css do %>
|
|
|
|
|
|
{Phoenix.HTML.raw("<style id=\"theme-css-live\">#{@generated_css}</style>")}
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<.skip_link />
|
|
|
|
|
|
|
|
|
|
|
|
<%= if @theme_settings.announcement_bar do %>
|
|
|
|
|
|
<.announcement_bar theme_settings={@theme_settings} />
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
|
|
|
|
|
|
<.shop_header
|
|
|
|
|
|
theme_settings={@theme_settings}
|
2026-03-03 14:52:31 +00:00
|
|
|
|
site_name={@site_name}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
logo_image={@logo_image}
|
|
|
|
|
|
header_image={@header_image}
|
|
|
|
|
|
active_page={@active_page}
|
|
|
|
|
|
mode={@mode}
|
|
|
|
|
|
cart_count={@cart_count}
|
2026-02-13 16:02:25 +00:00
|
|
|
|
is_admin={@is_admin}
|
2026-02-28 11:18:37 +00:00
|
|
|
|
header_nav_items={@header_nav_items}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
{render_slot(@inner_block)}
|
|
|
|
|
|
|
2026-02-13 08:27:26 +00:00
|
|
|
|
<.shop_footer
|
|
|
|
|
|
theme_settings={@theme_settings}
|
2026-03-03 14:52:31 +00:00
|
|
|
|
site_name={@site_name}
|
2026-02-13 08:27:26 +00:00
|
|
|
|
mode={@mode}
|
|
|
|
|
|
categories={assigns[:categories] || []}
|
2026-02-28 11:18:37 +00:00
|
|
|
|
footer_nav_items={@footer_nav_items}
|
2026-02-28 23:25:28 +00:00
|
|
|
|
newsletter_enabled={@newsletter_enabled}
|
|
|
|
|
|
newsletter_state={@newsletter_state}
|
2026-02-13 08:27:26 +00:00
|
|
|
|
/>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
<.cart_drawer
|
|
|
|
|
|
cart_items={@cart_items}
|
|
|
|
|
|
subtotal={@cart_subtotal}
|
2026-02-14 10:48:00 +00:00
|
|
|
|
total={@cart_total}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
cart_count={@cart_count}
|
|
|
|
|
|
mode={@mode}
|
|
|
|
|
|
open={@cart_drawer_open}
|
|
|
|
|
|
cart_status={@cart_status}
|
2026-02-14 10:48:00 +00:00
|
|
|
|
shipping_estimate={@shipping_estimate}
|
|
|
|
|
|
country_code={@country_code}
|
|
|
|
|
|
available_countries={@available_countries}
|
2026-03-04 14:02:49 +00:00
|
|
|
|
stripe_connected={@stripe_connected}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
/>
|
|
|
|
|
|
|
2026-02-13 07:29:19 +00:00
|
|
|
|
<.search_modal
|
|
|
|
|
|
hint_text={~s(Try a search – e.g. "mountain" or "notebook")}
|
|
|
|
|
|
search_query={@search_query}
|
|
|
|
|
|
search_results={@search_results}
|
2026-02-13 16:02:25 +00:00
|
|
|
|
search_open={@search_open}
|
2026-02-13 07:29:19 +00:00
|
|
|
|
/>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
2026-03-07 09:30:07 +00:00
|
|
|
|
<.mobile_nav_drawer
|
2026-02-28 11:18:37 +00:00
|
|
|
|
:if={!@error_page}
|
|
|
|
|
|
active_page={@active_page}
|
|
|
|
|
|
mode={@mode}
|
|
|
|
|
|
items={@header_nav_items}
|
2026-03-07 09:30:07 +00:00
|
|
|
|
categories={assigns[:categories] || []}
|
2026-02-28 11:18:37 +00:00
|
|
|
|
/>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders a mobile bottom navigation bar.
|
|
|
|
|
|
|
|
|
|
|
|
This component provides thumb-friendly navigation for mobile devices,
|
|
|
|
|
|
following modern UX best practices. It's hidden on larger screens where
|
|
|
|
|
|
the standard header navigation is used.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `active_page` - Required. The current page identifier (e.g., "home", "collection", "about", "contact").
|
|
|
|
|
|
* `mode` - Optional. Either `:live` (default) for real navigation or
|
|
|
|
|
|
`:preview` for theme preview mode with phx-click handlers.
|
|
|
|
|
|
* `cart_count` - Optional. Number of items in cart for badge display. Default: 0.
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.mobile_bottom_nav active_page="home" />
|
|
|
|
|
|
<.mobile_bottom_nav active_page="collection" mode={:preview} />
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :active_page, :string, required: true
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
attr :cart_count, :integer, default: 0
|
2026-02-28 11:18:37 +00:00
|
|
|
|
attr :items, :list, default: []
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
def mobile_bottom_nav(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<nav
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="mobile-bottom-nav"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-label="Main navigation"
|
|
|
|
|
|
>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<ul>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<.mobile_nav_item
|
2026-02-28 11:18:37 +00:00
|
|
|
|
:for={item <- @items}
|
|
|
|
|
|
icon={mobile_icon(item["slug"])}
|
|
|
|
|
|
label={item["label"]}
|
|
|
|
|
|
page={item["slug"] || ""}
|
|
|
|
|
|
href={item["href"]}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
active_page={@active_page}
|
2026-02-28 11:18:37 +00:00
|
|
|
|
active_pages={item["active_slugs"]}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
mode={@mode}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
attr :icon, :atom, required: true
|
|
|
|
|
|
attr :label, :string, required: true
|
|
|
|
|
|
attr :page, :string, required: true
|
|
|
|
|
|
attr :href, :string, required: true
|
|
|
|
|
|
attr :active_page, :string, required: true
|
|
|
|
|
|
attr :active_pages, :list, default: nil
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
|
|
|
|
|
|
defp mobile_nav_item(assigns) do
|
|
|
|
|
|
active_pages = assigns.active_pages || [assigns.page]
|
|
|
|
|
|
is_current = assigns.active_page in active_pages
|
|
|
|
|
|
assigns = assign(assigns, :is_current, is_current)
|
|
|
|
|
|
|
|
|
|
|
|
~H"""
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<li>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= if @mode == :preview do %>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page={@page}
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="mobile-nav-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-current={if @is_current, do: "page", else: nil}
|
|
|
|
|
|
>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<.nav_icon icon={@icon} />
|
|
|
|
|
|
<span>{@label}</span>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</a>
|
|
|
|
|
|
<% else %>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<.link
|
|
|
|
|
|
navigate={@href}
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="mobile-nav-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-current={if @is_current, do: "page", else: nil}
|
|
|
|
|
|
>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<.nav_icon icon={@icon} />
|
|
|
|
|
|
<span>{@label}</span>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2026-02-28 11:18:37 +00:00
|
|
|
|
defp mobile_icon("home"), do: :home
|
|
|
|
|
|
defp mobile_icon("collection"), do: :shop
|
|
|
|
|
|
defp mobile_icon("about"), do: :about
|
|
|
|
|
|
defp mobile_icon("contact"), do: :contact
|
|
|
|
|
|
defp mobile_icon(_), do: :page
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
defp nav_icon(%{icon: :home} = assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<svg
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="24"
|
|
|
|
|
|
height="24"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"></path>
|
|
|
|
|
|
<polyline points="9 22 9 12 15 12 15 22"></polyline>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
defp nav_icon(%{icon: :shop} = assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<svg
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="24"
|
|
|
|
|
|
height="24"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
>
|
|
|
|
|
|
<rect x="3" y="3" width="7" height="7"></rect>
|
|
|
|
|
|
<rect x="14" y="3" width="7" height="7"></rect>
|
|
|
|
|
|
<rect x="14" y="14" width="7" height="7"></rect>
|
|
|
|
|
|
<rect x="3" y="14" width="7" height="7"></rect>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
defp nav_icon(%{icon: :about} = assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<svg
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="24"
|
|
|
|
|
|
height="24"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
>
|
|
|
|
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
|
|
|
|
<line x1="12" y1="16" x2="12" y2="12"></line>
|
|
|
|
|
|
<line x1="12" y1="8" x2="12.01" y2="8"></line>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
defp nav_icon(%{icon: :contact} = assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<svg
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="24"
|
|
|
|
|
|
height="24"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
|
|
|
|
<polyline points="22,6 12,13 2,6"></polyline>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2026-02-28 11:18:37 +00:00
|
|
|
|
defp nav_icon(%{icon: :page} = assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="24"
|
|
|
|
|
|
height="24"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
|
|
|
|
|
<polyline points="14 2 14 8 20 8"></polyline>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
@doc """
|
2026-02-13 07:29:19 +00:00
|
|
|
|
Renders the search modal overlay with live search results.
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
2026-02-13 07:29:19 +00:00
|
|
|
|
* `hint_text` - Hint text shown when no query is entered.
|
|
|
|
|
|
* `search_query` - Current search query string.
|
|
|
|
|
|
* `search_results` - List of Product structs matching the query.
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
"""
|
|
|
|
|
|
attr :hint_text, :string, default: nil
|
2026-02-13 07:29:19 +00:00
|
|
|
|
attr :search_query, :string, default: ""
|
|
|
|
|
|
attr :search_results, :list, default: []
|
2026-02-13 16:02:25 +00:00
|
|
|
|
attr :search_open, :boolean, default: false
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
def search_modal(assigns) do
|
2026-02-18 21:23:15 +00:00
|
|
|
|
alias Berrypod.Cart
|
|
|
|
|
|
alias Berrypod.Products.{Product, ProductImage}
|
2026-02-13 07:29:19 +00:00
|
|
|
|
|
|
|
|
|
|
assigns =
|
|
|
|
|
|
assign(
|
|
|
|
|
|
assigns,
|
|
|
|
|
|
:results_with_images,
|
2026-02-13 08:27:26 +00:00
|
|
|
|
assigns.search_results
|
|
|
|
|
|
|> Enum.with_index()
|
|
|
|
|
|
|> Enum.map(fn {product, idx} ->
|
2026-02-13 07:29:19 +00:00
|
|
|
|
image = Product.primary_image(product)
|
2026-02-16 17:47:41 +00:00
|
|
|
|
%{product: product, image_url: ProductImage.url(image, 400), idx: idx}
|
2026-02-13 07:29:19 +00:00
|
|
|
|
end)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
~H"""
|
|
|
|
|
|
<div
|
|
|
|
|
|
id="search-modal"
|
|
|
|
|
|
class="search-modal"
|
2026-02-17 01:10:49 +00:00
|
|
|
|
style={"display: #{if @search_open, do: "flex", else: "none"};"}
|
2026-02-13 08:27:26 +00:00
|
|
|
|
phx-hook="SearchModal"
|
2026-02-13 16:21:51 +00:00
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
<div
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="search-panel"
|
2026-02-13 08:27:26 +00:00
|
|
|
|
onclick="event.stopPropagation()"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<div class="search-bar">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<svg
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="search-icon"
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
|
|
|
|
|
<circle cx="11" cy="11" r="8"></circle>
|
|
|
|
|
|
<path d="M21 21l-4.35-4.35"></path>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
id="search-input"
|
2026-02-13 07:29:19 +00:00
|
|
|
|
name="query"
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="search-input"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
placeholder="Search products..."
|
2026-02-13 07:29:19 +00:00
|
|
|
|
value={@search_query}
|
|
|
|
|
|
phx-keyup="search"
|
2026-02-13 08:27:26 +00:00
|
|
|
|
phx-debounce="150"
|
2026-02-13 07:29:19 +00:00
|
|
|
|
autocomplete="off"
|
2026-02-13 08:27:26 +00:00
|
|
|
|
role="combobox"
|
|
|
|
|
|
aria-expanded={to_string(@search_results != [])}
|
|
|
|
|
|
aria-controls="search-results-list"
|
|
|
|
|
|
aria-autocomplete="list"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
/>
|
2026-02-13 08:27:26 +00:00
|
|
|
|
<div
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="search-kbd"
|
2026-02-13 08:27:26 +00:00
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<kbd>⌘</kbd><kbd>K</kbd>
|
|
|
|
|
|
</div>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="search-close"
|
2026-02-13 16:21:51 +00:00
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-label="Close search"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
|
|
|
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
|
|
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2026-02-13 07:29:19 +00:00
|
|
|
|
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="search-results">
|
2026-02-13 07:29:19 +00:00
|
|
|
|
<%= cond do %>
|
|
|
|
|
|
<% @search_results != [] -> %>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<ul id="search-results-list" role="listbox" aria-label="Search results">
|
2026-02-13 08:27:26 +00:00
|
|
|
|
<li
|
|
|
|
|
|
:for={item <- @results_with_images}
|
|
|
|
|
|
id={"search-result-#{item.idx}"}
|
|
|
|
|
|
role="option"
|
|
|
|
|
|
aria-selected="false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<.link
|
|
|
|
|
|
navigate={"/products/#{item.product.slug || item.product.id}"}
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="search-result"
|
2026-02-13 16:21:51 +00:00
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
2026-02-13 07:29:19 +00:00
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
:if={item.image_url}
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="search-result-thumb"
|
2026-02-13 07:29:19 +00:00
|
|
|
|
>
|
|
|
|
|
|
<img
|
|
|
|
|
|
src={item.image_url}
|
|
|
|
|
|
alt={item.product.title}
|
|
|
|
|
|
loading="lazy"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<div class="search-result-details">
|
|
|
|
|
|
<p class="search-result-title">
|
2026-02-13 07:29:19 +00:00
|
|
|
|
{item.product.title}
|
|
|
|
|
|
</p>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<p class="search-result-meta">
|
2026-02-13 07:29:19 +00:00
|
|
|
|
{item.product.category}
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<span>
|
2026-02-13 07:29:19 +00:00
|
|
|
|
{Cart.format_price(item.product.cheapest_price)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
2026-02-13 08:27:26 +00:00
|
|
|
|
</.link>
|
2026-02-13 07:29:19 +00:00
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
<% String.length(@search_query) >= 2 -> %>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<div class="search-hint">
|
|
|
|
|
|
<p>No products found for "{@search_query}"</p>
|
2026-02-13 07:29:19 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<% @hint_text != nil -> %>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<div class="search-hint">
|
|
|
|
|
|
<p>{@hint_text}</p>
|
2026-02-13 07:29:19 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<% true -> %>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</div>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2026-03-07 09:30:07 +00:00
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the mobile navigation drawer.
|
|
|
|
|
|
|
|
|
|
|
|
A slide-out drawer containing the main navigation links for mobile users.
|
|
|
|
|
|
Triggered by the hamburger menu button in the header.
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :active_page, :string, required: true
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
attr :items, :list, default: []
|
|
|
|
|
|
attr :categories, :list, default: []
|
|
|
|
|
|
|
|
|
|
|
|
def mobile_nav_drawer(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<div
|
|
|
|
|
|
id="mobile-nav-drawer"
|
|
|
|
|
|
class="mobile-nav-drawer"
|
|
|
|
|
|
phx-hook="MobileNavDrawer"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="mobile-nav-backdrop"
|
|
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")}
|
|
|
|
|
|
>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<nav class="mobile-nav-panel" aria-label="Main navigation">
|
|
|
|
|
|
<div class="mobile-nav-header">
|
|
|
|
|
|
<span class="mobile-nav-title">Menu</span>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="mobile-nav-close"
|
|
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")}
|
|
|
|
|
|
aria-label="Close menu"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="24"
|
|
|
|
|
|
height="24"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
|
|
|
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
|
|
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<ul class="mobile-nav-links">
|
|
|
|
|
|
<li :for={item <- @items}>
|
|
|
|
|
|
<%= if @mode == :preview do %>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page={item["slug"]}
|
|
|
|
|
|
class="mobile-nav-link"
|
|
|
|
|
|
aria-current={@active_page in (item["active_slugs"] || [item["slug"]]) && "page"}
|
|
|
|
|
|
>
|
|
|
|
|
|
{item["label"]}
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<% else %>
|
|
|
|
|
|
<.link
|
|
|
|
|
|
navigate={item["href"]}
|
|
|
|
|
|
class="mobile-nav-link"
|
|
|
|
|
|
aria-current={@active_page in (item["active_slugs"] || [item["slug"]]) && "page"}
|
|
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")}
|
|
|
|
|
|
>
|
|
|
|
|
|
{item["label"]}
|
|
|
|
|
|
</.link>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
<%= if @categories != [] do %>
|
|
|
|
|
|
<div class="mobile-nav-section">
|
|
|
|
|
|
<span class="mobile-nav-section-title">Shop by category</span>
|
|
|
|
|
|
<ul class="mobile-nav-links">
|
|
|
|
|
|
<li :for={category <- @categories}>
|
|
|
|
|
|
<%= if @mode == :preview do %>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page="collection"
|
|
|
|
|
|
class="mobile-nav-link"
|
|
|
|
|
|
>
|
|
|
|
|
|
{category.name}
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<% else %>
|
|
|
|
|
|
<.link
|
|
|
|
|
|
navigate={"/collections/#{category.slug}"}
|
|
|
|
|
|
class="mobile-nav-link"
|
|
|
|
|
|
phx-click={
|
|
|
|
|
|
Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
{category.name}
|
|
|
|
|
|
</.link>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the shop footer with newsletter signup and links.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
2026-03-03 14:52:31 +00:00
|
|
|
|
* `theme_settings` - Required. The theme settings map.
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
* `mode` - Optional. Either `:live` (default) for real navigation or
|
|
|
|
|
|
`:preview` for theme preview mode with phx-click handlers.
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.shop_footer theme_settings={@theme_settings} />
|
|
|
|
|
|
<.shop_footer theme_settings={@theme_settings} mode={:preview} />
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :theme_settings, :map, required: true
|
2026-03-03 14:52:31 +00:00
|
|
|
|
attr :site_name, :string, required: true
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :mode, :atom, default: :live
|
2026-02-13 08:27:26 +00:00
|
|
|
|
attr :categories, :list, default: []
|
2026-02-28 11:18:37 +00:00
|
|
|
|
attr :footer_nav_items, :list, default: []
|
2026-02-28 23:25:28 +00:00
|
|
|
|
attr :newsletter_enabled, :boolean, default: false
|
|
|
|
|
|
attr :newsletter_state, :atom, default: :idle
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
def shop_footer(assigns) do
|
2026-02-13 08:27:26 +00:00
|
|
|
|
assigns = assign(assigns, :current_year, Date.utc_today().year)
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
~H"""
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<footer class="shop-footer">
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<div class="shop-footer-inner">
|
|
|
|
|
|
<div class="footer-grid">
|
2026-02-28 23:25:28 +00:00
|
|
|
|
<.newsletter_card
|
|
|
|
|
|
variant={:inline}
|
|
|
|
|
|
newsletter_enabled={@newsletter_enabled}
|
|
|
|
|
|
newsletter_state={@newsletter_state}
|
|
|
|
|
|
/>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
|
|
|
|
|
|
<div class="footer-links">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<div>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<h4 class="footer-heading">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Shop
|
|
|
|
|
|
</h4>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<ul class="footer-nav">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= if @mode == :preview do %>
|
|
|
|
|
|
<li>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page="collection"
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="footer-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
All products
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</li>
|
2026-02-11 08:17:19 +00:00
|
|
|
|
<%= for category <- @categories do %>
|
|
|
|
|
|
<li>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page="collection"
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="footer-link"
|
2026-02-11 08:17:19 +00:00
|
|
|
|
>
|
|
|
|
|
|
{category.name}
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
<% end %>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% else %>
|
|
|
|
|
|
<li>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<.link
|
|
|
|
|
|
navigate="/collections/all"
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="footer-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
All products
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</li>
|
2026-02-11 08:17:19 +00:00
|
|
|
|
<%= for category <- @categories do %>
|
|
|
|
|
|
<li>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<.link
|
|
|
|
|
|
navigate={"/collections/#{category.slug}"}
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="footer-link"
|
2026-02-11 08:17:19 +00:00
|
|
|
|
>
|
|
|
|
|
|
{category.name}
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
2026-02-11 08:17:19 +00:00
|
|
|
|
</li>
|
|
|
|
|
|
<% end %>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<h4 class="footer-heading">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Help
|
|
|
|
|
|
</h4>
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<ul class="footer-nav">
|
2026-02-28 11:18:37 +00:00
|
|
|
|
<li :for={item <- @footer_nav_items}>
|
|
|
|
|
|
<%= if @mode == :preview do %>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
2026-02-28 11:18:37 +00:00
|
|
|
|
phx-value-page={item["slug"]}
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="footer-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
2026-02-28 11:18:37 +00:00
|
|
|
|
{item["label"]}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</a>
|
2026-02-28 11:18:37 +00:00
|
|
|
|
<% else %>
|
|
|
|
|
|
<.link navigate={item["href"]} class="footer-link">
|
|
|
|
|
|
{item["label"]}
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
2026-02-28 11:18:37 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</li>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Bottom Bar -->
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<div class="footer-bottom">
|
|
|
|
|
|
<p class="footer-copyright">
|
2026-03-03 14:52:31 +00:00
|
|
|
|
© {@current_year} {@site_name}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</p>
|
2026-02-11 08:17:19 +00:00
|
|
|
|
<.social_links />
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the shop header with logo, navigation, and actions.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `theme_settings` - Required. The theme settings map.
|
|
|
|
|
|
* `logo_image` - Optional. The logo image struct (with id, is_svg fields).
|
|
|
|
|
|
* `header_image` - Optional. The header background image struct.
|
|
|
|
|
|
* `active_page` - Optional. Current page for nav highlighting.
|
|
|
|
|
|
* `mode` - Optional. Either `:live` (default) or `:preview`.
|
|
|
|
|
|
* `cart_count` - Optional. Number of items in cart. Defaults to 0.
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.shop_header theme_settings={@theme_settings} />
|
|
|
|
|
|
<.shop_header theme_settings={@theme_settings} mode={:preview} cart_count={2} />
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :theme_settings, :map, required: true
|
2026-03-03 14:52:31 +00:00
|
|
|
|
attr :site_name, :string, required: true
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :logo_image, :map, default: nil
|
|
|
|
|
|
attr :header_image, :map, default: nil
|
|
|
|
|
|
attr :active_page, :string, default: nil
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
attr :cart_count, :integer, default: 0
|
2026-02-13 16:02:25 +00:00
|
|
|
|
attr :is_admin, :boolean, default: false
|
2026-02-28 11:18:37 +00:00
|
|
|
|
attr :header_nav_items, :list, default: []
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
def shop_header(assigns) do
|
|
|
|
|
|
~H"""
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<header class="shop-header">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= if @theme_settings.header_background_enabled && @header_image do %>
|
|
|
|
|
|
<div style={header_background_style(@theme_settings, @header_image)} />
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
|
2026-03-07 09:30:07 +00:00
|
|
|
|
<%!-- Hamburger menu button (mobile only) --%>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="header-hamburger"
|
|
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("open-mobile-nav", to: "#mobile-nav-drawer")}
|
|
|
|
|
|
aria-label="Open menu"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="24"
|
|
|
|
|
|
height="24"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
|
|
|
|
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
|
|
|
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
|
|
|
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="shop-logo">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<.logo_content
|
|
|
|
|
|
theme_settings={@theme_settings}
|
2026-03-03 14:52:31 +00:00
|
|
|
|
site_name={@site_name}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
logo_image={@logo_image}
|
|
|
|
|
|
active_page={@active_page}
|
|
|
|
|
|
mode={@mode}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<nav class="shop-nav">
|
2026-02-28 11:18:37 +00:00
|
|
|
|
<.nav_item
|
|
|
|
|
|
:for={item <- @header_nav_items}
|
|
|
|
|
|
label={item["label"]}
|
|
|
|
|
|
href={item["href"]}
|
|
|
|
|
|
page={item["slug"] || ""}
|
|
|
|
|
|
active_page={@active_page}
|
|
|
|
|
|
active_pages={item["active_slugs"]}
|
|
|
|
|
|
mode={@mode}
|
|
|
|
|
|
/>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</nav>
|
|
|
|
|
|
|
2026-02-17 09:03:35 +00:00
|
|
|
|
<div class="shop-actions">
|
2026-03-07 09:30:07 +00:00
|
|
|
|
<%!-- Admin cog: always visible for admins, links to admin dashboard --%>
|
2026-02-13 16:02:25 +00:00
|
|
|
|
<.link
|
|
|
|
|
|
:if={@is_admin}
|
|
|
|
|
|
href="/admin"
|
2026-02-17 09:03:35 +00:00
|
|
|
|
class="header-icon-btn"
|
2026-03-07 09:30:07 +00:00
|
|
|
|
aria-label="Admin dashboard"
|
2026-02-13 16:02:25 +00:00
|
|
|
|
>
|
2026-03-07 09:30:07 +00:00
|
|
|
|
<.admin_cog_svg />
|
2026-02-13 16:02:25 +00:00
|
|
|
|
</.link>
|
2026-02-24 22:56:19 +00:00
|
|
|
|
<a
|
|
|
|
|
|
href="/search"
|
2026-02-13 16:21:51 +00:00
|
|
|
|
phx-click={Phoenix.LiveView.JS.dispatch("open-search", to: "#search-modal")}
|
2026-02-24 22:56:19 +00:00
|
|
|
|
class="header-icon-btn"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-label="Search"
|
|
|
|
|
|
>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<svg
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<circle cx="11" cy="11" r="8"></circle>
|
|
|
|
|
|
<path d="M21 21l-4.35-4.35"></path>
|
|
|
|
|
|
</svg>
|
2026-02-24 22:56:19 +00:00
|
|
|
|
</a>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="/cart"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
phx-click={open_cart_drawer_js()}
|
2026-02-24 22:56:19 +00:00
|
|
|
|
class="header-icon-btn"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-label="Cart"
|
|
|
|
|
|
>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<svg
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"></path>
|
|
|
|
|
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
|
|
|
|
<path d="M16 10a4 4 0 01-8 0"></path>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
<%= if @cart_count > 0 do %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<span class="cart-badge">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
{@cart_count}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
<span class="sr-only">Cart ({@cart_count})</span>
|
2026-02-24 22:56:19 +00:00
|
|
|
|
</a>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
defp logo_url(logo_image, %{logo_recolor: true, logo_color: color}) when logo_image.is_svg do
|
|
|
|
|
|
clean_color = String.trim_leading(color, "#")
|
|
|
|
|
|
"/images/#{logo_image.id}/recolored/#{clean_color}"
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2026-02-16 17:47:41 +00:00
|
|
|
|
defp logo_url(logo_image, _), do: "/image_cache/#{logo_image.id}.webp"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
# Logo content that links to home, except when already on home page.
|
|
|
|
|
|
# This follows accessibility best practices - current page should not be a link.
|
|
|
|
|
|
attr :theme_settings, :map, required: true
|
2026-03-03 14:52:31 +00:00
|
|
|
|
attr :site_name, :string, required: true
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :logo_image, :map, default: nil
|
|
|
|
|
|
attr :active_page, :string, default: nil
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
|
|
|
|
|
|
defp logo_content(assigns) do
|
|
|
|
|
|
is_home = assigns.active_page == "home"
|
|
|
|
|
|
assigns = assign(assigns, :is_home, is_home)
|
|
|
|
|
|
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<%= if @is_home do %>
|
2026-03-03 14:52:31 +00:00
|
|
|
|
<.logo_inner theme_settings={@theme_settings} site_name={@site_name} logo_image={@logo_image} />
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% else %>
|
|
|
|
|
|
<%= if @mode == :preview do %>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page="home"
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class="shop-logo-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
2026-03-03 14:52:31 +00:00
|
|
|
|
<.logo_inner
|
|
|
|
|
|
theme_settings={@theme_settings}
|
|
|
|
|
|
site_name={@site_name}
|
|
|
|
|
|
logo_image={@logo_image}
|
|
|
|
|
|
/>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</a>
|
|
|
|
|
|
<% else %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<.link navigate="/" class="shop-logo-link">
|
2026-03-03 14:52:31 +00:00
|
|
|
|
<.logo_inner
|
|
|
|
|
|
theme_settings={@theme_settings}
|
|
|
|
|
|
site_name={@site_name}
|
|
|
|
|
|
logo_image={@logo_image}
|
|
|
|
|
|
/>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
attr :theme_settings, :map, required: true
|
2026-03-03 14:52:31 +00:00
|
|
|
|
attr :site_name, :string, required: true
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :logo_image, :map, default: nil
|
|
|
|
|
|
|
|
|
|
|
|
defp logo_inner(assigns) do
|
2026-03-08 22:40:08 +00:00
|
|
|
|
# Show logo if enabled and image exists
|
|
|
|
|
|
show_logo = assigns.theme_settings.show_logo && assigns.logo_image
|
|
|
|
|
|
|
|
|
|
|
|
# Show site name if enabled, or as fallback when logo should show but image is missing
|
|
|
|
|
|
show_site_name =
|
|
|
|
|
|
assigns.theme_settings.show_site_name ||
|
|
|
|
|
|
(assigns.theme_settings.show_logo && !assigns.logo_image)
|
|
|
|
|
|
|
|
|
|
|
|
assigns =
|
|
|
|
|
|
assigns
|
|
|
|
|
|
|> assign(:show_logo, show_logo)
|
|
|
|
|
|
|> assign(:show_site_name, show_site_name)
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
~H"""
|
2026-03-08 22:40:08 +00:00
|
|
|
|
<%= if @show_logo do %>
|
|
|
|
|
|
<img
|
|
|
|
|
|
src={logo_url(@logo_image, @theme_settings)}
|
|
|
|
|
|
alt={@site_name}
|
|
|
|
|
|
class="shop-logo-img"
|
|
|
|
|
|
style={"height: #{@theme_settings.logo_size}px;"}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
<%= if @show_site_name do %>
|
|
|
|
|
|
<span class="shop-logo-text">
|
|
|
|
|
|
{@site_name}
|
|
|
|
|
|
</span>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
defp header_background_style(settings, header_image) do
|
|
|
|
|
|
"position: absolute; top: 0; left: 0; right: 0; bottom: 0; " <>
|
2026-02-16 17:47:41 +00:00
|
|
|
|
"background-image: url('/image_cache/#{header_image.id}.webp'); " <>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
"background-size: #{settings.header_zoom}%; " <>
|
|
|
|
|
|
"background-position: #{settings.header_position_x}% #{settings.header_position_y}%; " <>
|
|
|
|
|
|
"background-repeat: no-repeat; z-index: 0;"
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Navigation item that renders as a span (not a link) when on the current page.
|
|
|
|
|
|
# This follows accessibility best practices - current page should not be a link.
|
|
|
|
|
|
attr :label, :string, required: true
|
|
|
|
|
|
attr :page, :string, required: true
|
|
|
|
|
|
attr :active_page, :string, required: true
|
|
|
|
|
|
attr :href, :string, default: nil
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
attr :active_pages, :list, default: nil
|
|
|
|
|
|
|
|
|
|
|
|
defp nav_item(assigns) do
|
|
|
|
|
|
# Allow matching multiple pages (e.g., "Shop" is active for both collection and pdp)
|
|
|
|
|
|
active_pages = assigns.active_pages || [assigns.page]
|
|
|
|
|
|
is_current = assigns.active_page in active_pages
|
|
|
|
|
|
assigns = assign(assigns, :is_current, is_current)
|
|
|
|
|
|
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<%= if @is_current do %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<span class="nav-link" aria-current="page">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
{@label}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<% else %>
|
|
|
|
|
|
<%= if @mode == :preview do %>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href="#"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page={@page}
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class="nav-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
{@label}
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<% else %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<.link navigate={@href} class="nav-link">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
{@label}
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2026-02-27 16:22:35 +00:00
|
|
|
|
defp edit_pencil_svg(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="1.5"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
defp open_cart_drawer_js do
|
|
|
|
|
|
Phoenix.LiveView.JS.push("open_cart_drawer")
|
|
|
|
|
|
end
|
2026-03-07 09:30:07 +00:00
|
|
|
|
|
|
|
|
|
|
# ── Editor sheet ────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
2026-03-09 09:01:21 +00:00
|
|
|
|
Renders the unified editor sheet for page/theme/settings editing.
|
2026-03-07 09:30:07 +00:00
|
|
|
|
|
|
|
|
|
|
The sheet is anchored to the bottom edge on mobile (<768px) and the right edge
|
|
|
|
|
|
on desktop (≥768px). It has three states on mobile (collapsed, partial, full)
|
|
|
|
|
|
and two states on desktop (collapsed, open).
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
2026-03-09 09:01:21 +00:00
|
|
|
|
* `editing` - Whether page edit mode is active.
|
|
|
|
|
|
* `theme_editing` - Whether theme edit mode is active.
|
|
|
|
|
|
* `editor_dirty` - Whether there are unsaved page changes.
|
2026-03-07 09:30:07 +00:00
|
|
|
|
* `editor_sheet_state` - Current state (:collapsed, :partial, :full, or :open).
|
2026-03-09 09:01:21 +00:00
|
|
|
|
* `editor_active_tab` - Current tab (:page, :theme, :settings).
|
|
|
|
|
|
* `has_editable_page` - Whether the current page has editable blocks.
|
2026-03-07 09:30:07 +00:00
|
|
|
|
|
|
|
|
|
|
## Slots
|
|
|
|
|
|
|
|
|
|
|
|
* `inner_block` - The editor content (block list, settings, etc.).
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :editing, :boolean, default: false
|
2026-03-09 09:01:21 +00:00
|
|
|
|
attr :theme_editing, :boolean, default: false
|
2026-03-07 09:30:07 +00:00
|
|
|
|
attr :editor_dirty, :boolean, default: false
|
|
|
|
|
|
attr :editor_sheet_state, :atom, default: :collapsed
|
|
|
|
|
|
attr :editor_save_status, :atom, default: :idle
|
2026-03-09 09:01:21 +00:00
|
|
|
|
attr :editor_active_tab, :atom, default: :page
|
|
|
|
|
|
attr :has_editable_page, :boolean, default: false
|
2026-03-07 09:30:07 +00:00
|
|
|
|
|
|
|
|
|
|
slot :inner_block
|
|
|
|
|
|
|
|
|
|
|
|
def editor_sheet(assigns) do
|
2026-03-09 09:01:21 +00:00
|
|
|
|
# Determine panel title based on active tab
|
|
|
|
|
|
title =
|
|
|
|
|
|
case assigns.editor_active_tab do
|
|
|
|
|
|
:page -> "Page"
|
|
|
|
|
|
:theme -> "Theme"
|
|
|
|
|
|
:settings -> "Settings"
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Any editing mode active
|
|
|
|
|
|
any_editing = assigns.editing || assigns.theme_editing
|
|
|
|
|
|
|
|
|
|
|
|
assigns =
|
|
|
|
|
|
assigns
|
|
|
|
|
|
|> assign(:title, title)
|
|
|
|
|
|
|> assign(:any_editing, any_editing)
|
|
|
|
|
|
|
2026-03-07 09:30:07 +00:00
|
|
|
|
~H"""
|
2026-03-07 19:01:32 +00:00
|
|
|
|
<%!-- Floating action button: always visible when panel is closed --%>
|
|
|
|
|
|
<button
|
|
|
|
|
|
:if={@editor_sheet_state == :collapsed}
|
|
|
|
|
|
type="button"
|
2026-03-09 09:01:21 +00:00
|
|
|
|
phx-click={if @any_editing, do: "editor_set_sheet_state", else: "editor_set_tab"}
|
|
|
|
|
|
phx-value-state={if @any_editing, do: "open", else: nil}
|
|
|
|
|
|
phx-value-tab={
|
|
|
|
|
|
if @any_editing, do: nil, else: if(@has_editable_page, do: "page", else: "theme")
|
|
|
|
|
|
}
|
2026-03-07 19:01:32 +00:00
|
|
|
|
class="editor-fab"
|
2026-03-09 09:01:21 +00:00
|
|
|
|
aria-label={if @any_editing, do: "Show editor", else: "Edit"}
|
2026-03-07 19:01:32 +00:00
|
|
|
|
>
|
|
|
|
|
|
<.edit_pencil_svg />
|
2026-03-09 09:01:21 +00:00
|
|
|
|
<span>{if @any_editing, do: "Show editor", else: "Edit"}</span>
|
2026-03-07 19:01:32 +00:00
|
|
|
|
<span :if={@editing && @editor_dirty} class="editor-fab-dirty" aria-label="Unsaved changes" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
2026-03-09 09:01:21 +00:00
|
|
|
|
<%!-- Overlay to catch taps outside the panel --%>
|
|
|
|
|
|
<div
|
|
|
|
|
|
:if={@editor_sheet_state == :open}
|
|
|
|
|
|
class="editor-overlay"
|
|
|
|
|
|
phx-click="editor_set_sheet_state"
|
|
|
|
|
|
phx-value-state="collapsed"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-03-07 19:01:32 +00:00
|
|
|
|
<%!-- Editor panel: slides in/out --%>
|
2026-03-07 09:30:07 +00:00
|
|
|
|
<aside
|
2026-03-07 19:01:32 +00:00
|
|
|
|
id="editor-panel"
|
|
|
|
|
|
class="editor-panel"
|
2026-03-07 09:30:07 +00:00
|
|
|
|
role="region"
|
2026-03-09 09:01:21 +00:00
|
|
|
|
aria-label="Site editor"
|
2026-03-07 19:01:32 +00:00
|
|
|
|
aria-hidden={to_string(@editor_sheet_state == :collapsed)}
|
2026-03-07 09:30:07 +00:00
|
|
|
|
data-state={@editor_sheet_state}
|
2026-03-09 09:01:21 +00:00
|
|
|
|
data-editing={to_string(@any_editing)}
|
2026-03-07 09:30:07 +00:00
|
|
|
|
phx-hook="EditorSheet"
|
|
|
|
|
|
>
|
2026-03-09 09:01:21 +00:00
|
|
|
|
<%!-- Drag handle for mobile resizing --%>
|
|
|
|
|
|
<div class="editor-panel-drag-handle" data-drag-handle>
|
|
|
|
|
|
<div class="editor-panel-drag-handle-bar" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-07 19:01:32 +00:00
|
|
|
|
<div class="editor-panel-header">
|
|
|
|
|
|
<div class="editor-panel-header-left">
|
2026-03-09 09:01:21 +00:00
|
|
|
|
<span class="editor-panel-title">{@title}</span>
|
|
|
|
|
|
<span :if={@editing && @editor_dirty} class="editor-panel-dirty" aria-live="polite">
|
2026-03-07 19:01:32 +00:00
|
|
|
|
<span class="editor-panel-dirty-dot" aria-hidden="true" />
|
|
|
|
|
|
<span>Unsaved</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="editor-panel-header-actions">
|
2026-03-07 09:30:07 +00:00
|
|
|
|
<button
|
2026-03-09 09:01:21 +00:00
|
|
|
|
:if={@editor_active_tab == :page && @editor_save_status == :saved}
|
2026-03-07 09:30:07 +00:00
|
|
|
|
type="button"
|
2026-03-07 19:01:32 +00:00
|
|
|
|
class="admin-btn admin-btn-sm admin-btn-ghost"
|
|
|
|
|
|
disabled
|
2026-03-07 09:30:07 +00:00
|
|
|
|
>
|
2026-03-07 19:01:32 +00:00
|
|
|
|
Saved ✓
|
2026-03-07 09:30:07 +00:00
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
2026-03-09 09:01:21 +00:00
|
|
|
|
:if={@editor_active_tab == :page && @editor_save_status != :saved}
|
2026-03-07 09:30:07 +00:00
|
|
|
|
type="button"
|
2026-03-07 19:01:32 +00:00
|
|
|
|
phx-click="editor_save"
|
|
|
|
|
|
class={["admin-btn admin-btn-sm", @editor_dirty && "admin-btn-primary"]}
|
|
|
|
|
|
disabled={!@editor_dirty}
|
2026-03-07 09:30:07 +00:00
|
|
|
|
>
|
2026-03-07 19:01:32 +00:00
|
|
|
|
Save
|
2026-03-07 09:30:07 +00:00
|
|
|
|
</button>
|
2026-03-09 09:01:21 +00:00
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
phx-click="editor_set_sheet_state"
|
|
|
|
|
|
phx-value-state="collapsed"
|
|
|
|
|
|
class="editor-panel-close"
|
|
|
|
|
|
aria-label="Close editor"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
2026-03-07 19:01:32 +00:00
|
|
|
|
</div>
|
2026-03-07 09:30:07 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-09 09:01:21 +00:00
|
|
|
|
<%!-- Tab bar --%>
|
|
|
|
|
|
<div class="editor-tabs" role="tablist">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
role="tab"
|
|
|
|
|
|
phx-click="editor_set_tab"
|
|
|
|
|
|
phx-value-tab="page"
|
|
|
|
|
|
class={["editor-tab", @editor_active_tab == :page && "editor-tab-active"]}
|
|
|
|
|
|
aria-selected={to_string(@editor_active_tab == :page)}
|
|
|
|
|
|
disabled={!@has_editable_page}
|
|
|
|
|
|
title={
|
|
|
|
|
|
if @has_editable_page, do: "Edit page blocks", else: "This page has no editable blocks"
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
Page
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
role="tab"
|
|
|
|
|
|
phx-click="editor_set_tab"
|
|
|
|
|
|
phx-value-tab="theme"
|
|
|
|
|
|
class={["editor-tab", @editor_active_tab == :theme && "editor-tab-active"]}
|
|
|
|
|
|
aria-selected={to_string(@editor_active_tab == :theme)}
|
|
|
|
|
|
>
|
|
|
|
|
|
Theme
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
role="tab"
|
|
|
|
|
|
phx-click="editor_set_tab"
|
|
|
|
|
|
phx-value-tab="settings"
|
|
|
|
|
|
class={["editor-tab", @editor_active_tab == :settings && "editor-tab-active"]}
|
|
|
|
|
|
aria-selected={to_string(@editor_active_tab == :settings)}
|
|
|
|
|
|
>
|
|
|
|
|
|
Settings
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-07 19:01:32 +00:00
|
|
|
|
<div class="editor-panel-content">
|
2026-03-07 09:30:07 +00:00
|
|
|
|
{render_slot(@inner_block)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
|
|
<%!-- Live region for screen reader announcements --%>
|
|
|
|
|
|
<div id="editor-live-region" class="sr-only" aria-live="polite" aria-atomic="true" />
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# ── Admin rail (deprecated) ────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the admin rail with edit and admin icons.
|
|
|
|
|
|
|
|
|
|
|
|
This thin vertical bar appears on the left edge of the page for logged-in admins.
|
|
|
|
|
|
The edit button toggles the page editor, and the cog links to the admin dashboard.
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :editing, :boolean, default: false
|
|
|
|
|
|
attr :editor_dirty, :boolean, default: false
|
|
|
|
|
|
attr :editor_sidebar_open, :boolean, default: true
|
|
|
|
|
|
slot :editor_sidebar
|
|
|
|
|
|
slot :inner_block, required: true
|
|
|
|
|
|
|
|
|
|
|
|
def admin_rail(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="admin-rail-layout"
|
|
|
|
|
|
data-editing={to_string(@editing)}
|
|
|
|
|
|
data-sidebar-open={to_string(@editor_sidebar_open)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<aside class="admin-rail" aria-label="Admin tools">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
phx-click="editor_toggle_editing"
|
|
|
|
|
|
class={["admin-rail-btn", @editing && "admin-rail-btn-active"]}
|
|
|
|
|
|
aria-label={if @editing, do: "Close editor", else: "Edit page"}
|
|
|
|
|
|
aria-pressed={to_string(@editing)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<.edit_pencil_svg />
|
|
|
|
|
|
<span
|
|
|
|
|
|
:if={@editing && @editor_dirty}
|
|
|
|
|
|
class="admin-rail-dirty-dot"
|
|
|
|
|
|
aria-label="Unsaved changes"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<.link href="/admin" class="admin-rail-btn" aria-label="Admin dashboard">
|
|
|
|
|
|
<.admin_cog_svg />
|
|
|
|
|
|
</.link>
|
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
|
|
<aside :if={@editing} class="admin-rail-sidebar" aria-label="Page editor">
|
|
|
|
|
|
{render_slot(@editor_sidebar)}
|
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
|
|
<%!-- Backdrop to close sidebar on mobile --%>
|
|
|
|
|
|
<div
|
|
|
|
|
|
:if={@editing && @editor_sidebar_open}
|
|
|
|
|
|
class="admin-rail-backdrop"
|
|
|
|
|
|
phx-click="editor_toggle_sidebar"
|
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="admin-rail-content">
|
|
|
|
|
|
{render_slot(@inner_block)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def admin_cog_svg(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<svg
|
|
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="1.5"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<path
|
|
|
|
|
|
stroke-linecap="round"
|
|
|
|
|
|
stroke-linejoin="round"
|
|
|
|
|
|
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
end
|