add image uploads to on-site theme editor and fix scroll on navigation
All checks were successful
deploy / deploy (push) Successful in 1m27s
All checks were successful
deploy / deploy (push) Successful in 1m27s
Phase 4 of unified editing: image upload handling in hook context. - Configure uploads in Shop.Page mount for logo, header, icon - Add upload UI components to theme_editor compact_editor - Pass uploads through page_renderer to theme editor - Add cancel_upload handler to PageEditorHook Also fixes scroll position not resetting on patch navigation: - Push scroll-top event when path changes in handle_params - JS listener scrolls window to top instantly Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
89c411e0fc
commit
378b3fdb6b
@ -91,7 +91,7 @@ Extend the existing page editor (PageEditorHook + editor_sheet) to include theme
|
||||
| 2 | Add 3-tab UI to editor panel (Page/Theme/Settings) | 2h | done |
|
||||
| 3 | Extract theme editor into reusable component | 3h | done |
|
||||
| 3b | Create settings editor component | 2h | done |
|
||||
| 4 | Image upload handling in hook context | 2h | planned |
|
||||
| 4 | Image upload handling in hook context | 2h | done |
|
||||
| 5 | URL-based mode activation (?edit=theme) | 1h | planned |
|
||||
| 6 | Admin routing redirect | 30m | planned |
|
||||
| 7 | Polish and testing | 2h | planned |
|
||||
|
||||
@ -951,6 +951,11 @@ window.addEventListener("phx:scroll-preview-top", (e) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Scroll to top on page navigation (patch navigation within LiveView)
|
||||
window.addEventListener("phx:scroll-top", () => {
|
||||
window.scrollTo({top: 0, behavior: 'instant'})
|
||||
})
|
||||
|
||||
// connect if there are any LiveViews on the page
|
||||
liveSocket.connect()
|
||||
|
||||
|
||||
@ -203,12 +203,29 @@ defmodule BerrypodWeb.ShopComponents.ThemeEditor do
|
||||
attr :site_name, :string, default: ""
|
||||
attr :customise_open, :boolean, default: false
|
||||
attr :event_prefix, :string, default: "theme_"
|
||||
attr :uploads, :map, default: nil
|
||||
attr :logo_image, :map, default: nil
|
||||
attr :header_image, :map, default: nil
|
||||
attr :icon_image, :map, default: nil
|
||||
attr :contrast_warning, :atom, default: :ok
|
||||
|
||||
def compact_editor(assigns) do
|
||||
~H"""
|
||||
<div class="editor-theme-content">
|
||||
<%= if @theme_settings do %>
|
||||
<.shop_name_input site_name={@site_name} event_prefix={@event_prefix} />
|
||||
|
||||
<.branding_section
|
||||
theme_settings={@theme_settings}
|
||||
uploads={@uploads}
|
||||
logo_image={@logo_image}
|
||||
header_image={@header_image}
|
||||
icon_image={@icon_image}
|
||||
contrast_warning={@contrast_warning}
|
||||
site_name={@site_name}
|
||||
event_prefix={@event_prefix}
|
||||
/>
|
||||
|
||||
<.preset_grid presets={@presets} active_preset={@active_preset} event_prefix={@event_prefix} />
|
||||
|
||||
<.color_picker
|
||||
@ -235,12 +252,6 @@ defmodule BerrypodWeb.ShopComponents.ThemeEditor do
|
||||
customise_open={@customise_open}
|
||||
event_prefix={@event_prefix}
|
||||
/>
|
||||
|
||||
<div class="theme-section">
|
||||
<p class="admin-text-secondary">
|
||||
For logo and header image uploads, <a href="/admin/theme" class="admin-link">visit the full theme editor</a>.
|
||||
</p>
|
||||
</div>
|
||||
<% else %>
|
||||
<p class="admin-text-secondary">Loading theme settings...</p>
|
||||
<% end %>
|
||||
@ -248,6 +259,334 @@ defmodule BerrypodWeb.ShopComponents.ThemeEditor do
|
||||
"""
|
||||
end
|
||||
|
||||
# ── Branding Section (Logo, Header, Icon) ───────────────────────────
|
||||
|
||||
attr :theme_settings, :map, required: true
|
||||
attr :uploads, :map, default: nil
|
||||
attr :logo_image, :map, default: nil
|
||||
attr :header_image, :map, default: nil
|
||||
attr :icon_image, :map, default: nil
|
||||
attr :contrast_warning, :atom, default: :ok
|
||||
attr :site_name, :string, default: ""
|
||||
attr :event_prefix, :string, default: "theme_"
|
||||
|
||||
defp branding_section(assigns) do
|
||||
~H"""
|
||||
<div class="theme-section theme-section-compact">
|
||||
<label class="theme-section-label">Logo & branding</label>
|
||||
|
||||
<div class="admin-stack admin-stack-sm theme-field">
|
||||
<label class="admin-check-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={@theme_settings.show_site_name}
|
||||
phx-click={@event_prefix <> "toggle_setting"}
|
||||
phx-value-field="show_site_name"
|
||||
class="admin-checkbox admin-checkbox-sm"
|
||||
/>
|
||||
<span class="theme-check-text">Show shop name</span>
|
||||
</label>
|
||||
|
||||
<label class="admin-check-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={@theme_settings.show_logo}
|
||||
phx-click={@event_prefix <> "toggle_setting"}
|
||||
phx-value-field="show_logo"
|
||||
class="admin-checkbox admin-checkbox-sm"
|
||||
/>
|
||||
<span class="theme-check-text">Show logo</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<%= if @theme_settings.show_logo && @uploads do %>
|
||||
<.logo_upload_section
|
||||
uploads={@uploads}
|
||||
logo_image={@logo_image}
|
||||
theme_settings={@theme_settings}
|
||||
site_name={@site_name}
|
||||
event_prefix={@event_prefix}
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
<label class="admin-check-label theme-field">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={@theme_settings.header_background_enabled}
|
||||
phx-click={@event_prefix <> "update_setting"}
|
||||
phx-value-field="header_background_enabled"
|
||||
phx-value-setting_value={
|
||||
if @theme_settings.header_background_enabled, do: "false", else: "true"
|
||||
}
|
||||
class="admin-checkbox admin-checkbox-sm"
|
||||
/>
|
||||
<span class="theme-check-text">Header background image</span>
|
||||
</label>
|
||||
|
||||
<%= if @theme_settings.header_background_enabled && @uploads do %>
|
||||
<.header_upload_section
|
||||
uploads={@uploads}
|
||||
header_image={@header_image}
|
||||
theme_settings={@theme_settings}
|
||||
contrast_warning={@contrast_warning}
|
||||
event_prefix={@event_prefix}
|
||||
/>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# Logo upload sub-section
|
||||
attr :uploads, :map, required: true
|
||||
attr :logo_image, :map, default: nil
|
||||
attr :theme_settings, :map, required: true
|
||||
attr :site_name, :string, default: ""
|
||||
attr :event_prefix, :string, default: "theme_"
|
||||
|
||||
defp logo_upload_section(assigns) do
|
||||
~H"""
|
||||
<div class="theme-subsection">
|
||||
<span class="theme-slider-label theme-block-label">Upload logo (SVG or PNG)</span>
|
||||
<div class="admin-row admin-row-lg">
|
||||
<form phx-change="noop" phx-submit="noop" class="admin-fill">
|
||||
<label class="theme-upload-label theme-upload-label-compact">
|
||||
<span>Choose file...</span>
|
||||
<.live_file_input upload={@uploads.theme_logo_upload} class="hidden" />
|
||||
</label>
|
||||
</form>
|
||||
<%= if @logo_image do %>
|
||||
<div class="theme-thumb theme-thumb-logo theme-thumb-compact">
|
||||
<img src={"/image_cache/#{@logo_image.id}.webp"} alt={@site_name} />
|
||||
<button
|
||||
type="button"
|
||||
phx-click={@event_prefix <> "remove_logo"}
|
||||
class="theme-remove-btn"
|
||||
title="Remove logo"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= for entry <- @uploads.theme_logo_upload.entries do %>
|
||||
<.upload_progress entry={entry} upload_name="theme_logo_upload" />
|
||||
<%= for err <- upload_errors(@uploads.theme_logo_upload, entry) do %>
|
||||
<p class="theme-error-text">{error_to_string(err)}</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= for err <- upload_errors(@uploads.theme_logo_upload) do %>
|
||||
<p class="theme-error-text">{error_to_string(err)}</p>
|
||||
<% end %>
|
||||
|
||||
<%= if @logo_image do %>
|
||||
<form
|
||||
phx-change={@event_prefix <> "update_setting"}
|
||||
phx-value-field="logo_size"
|
||||
class="theme-subfield"
|
||||
>
|
||||
<div class="theme-slider-header">
|
||||
<span class="theme-slider-label">Logo size</span>
|
||||
<span class="theme-slider-value">{@theme_settings.logo_size}px</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="24"
|
||||
max="120"
|
||||
value={@theme_settings.logo_size}
|
||||
name="logo_size"
|
||||
class="admin-range"
|
||||
/>
|
||||
</form>
|
||||
|
||||
<%= if @logo_image.is_svg do %>
|
||||
<div class="theme-subfield">
|
||||
<label class="admin-check-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={@theme_settings.logo_recolor}
|
||||
phx-click={@event_prefix <> "update_setting"}
|
||||
phx-value-field="logo_recolor"
|
||||
phx-value-setting_value={if @theme_settings.logo_recolor, do: "false", else: "true"}
|
||||
class="admin-checkbox admin-checkbox-sm"
|
||||
/>
|
||||
<span class="theme-check-text">Recolour logo</span>
|
||||
</label>
|
||||
|
||||
<%= if @theme_settings.logo_recolor do %>
|
||||
<form
|
||||
id="logo-color-form-compact"
|
||||
phx-change={@event_prefix <> "update_color"}
|
||||
phx-value-field="logo_color"
|
||||
phx-hook="ColorSync"
|
||||
class="theme-color-row theme-subfield-sm"
|
||||
>
|
||||
<input
|
||||
type="color"
|
||||
name="value"
|
||||
value={@theme_settings.logo_color}
|
||||
class="theme-color-swatch theme-color-swatch-sm"
|
||||
/>
|
||||
<span class="theme-color-value">{@theme_settings.logo_color}</span>
|
||||
</form>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# Header upload sub-section
|
||||
attr :uploads, :map, required: true
|
||||
attr :header_image, :map, default: nil
|
||||
attr :theme_settings, :map, required: true
|
||||
attr :contrast_warning, :atom, default: :ok
|
||||
attr :event_prefix, :string, default: "theme_"
|
||||
|
||||
defp header_upload_section(assigns) do
|
||||
~H"""
|
||||
<div class="theme-subsection">
|
||||
<span class="theme-slider-label theme-block-label">Upload header image</span>
|
||||
<form phx-change="noop" phx-submit="noop">
|
||||
<label class="theme-upload-label theme-upload-label-compact">
|
||||
<span>Choose file...</span>
|
||||
<.live_file_input upload={@uploads.theme_header_upload} class="hidden" />
|
||||
</label>
|
||||
</form>
|
||||
|
||||
<%= if @header_image do %>
|
||||
<div class="theme-thumb theme-thumb-cover theme-thumb-header theme-thumb-compact">
|
||||
<img src={"/image_cache/#{@header_image.id}.webp"} alt="" />
|
||||
<button
|
||||
type="button"
|
||||
phx-click={@event_prefix <> "remove_header"}
|
||||
class="theme-remove-btn"
|
||||
title="Remove header background"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<%= if @contrast_warning != :ok do %>
|
||||
<div class="theme-contrast-warning theme-contrast-warning-compact">
|
||||
<strong>
|
||||
<%= if @contrast_warning == :poor do %>
|
||||
Text may be hard to read
|
||||
<% else %>
|
||||
Text contrast could be better
|
||||
<% end %>
|
||||
</strong>
|
||||
<p>
|
||||
Try switching to a
|
||||
<%= if @theme_settings.mood == "dark" do %>
|
||||
lighter mood
|
||||
<% else %>
|
||||
dark mood
|
||||
<% end %>
|
||||
or choosing a different image.
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="admin-stack admin-stack-md theme-subfield">
|
||||
<form phx-change={@event_prefix <> "update_setting"} phx-value-field="header_zoom">
|
||||
<div class="theme-slider-header">
|
||||
<span class="theme-slider-label">Zoom</span>
|
||||
<span class="theme-slider-value">{@theme_settings.header_zoom}%</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="100"
|
||||
max="200"
|
||||
value={@theme_settings.header_zoom}
|
||||
name="header_zoom"
|
||||
class="admin-range"
|
||||
/>
|
||||
</form>
|
||||
<%= if @theme_settings.header_zoom > 100 do %>
|
||||
<form
|
||||
phx-change={@event_prefix <> "update_setting"}
|
||||
phx-value-field="header_position_x"
|
||||
>
|
||||
<div class="theme-slider-header">
|
||||
<span class="theme-slider-label">Horizontal position</span>
|
||||
<span class="theme-slider-value">{@theme_settings.header_position_x}%</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={@theme_settings.header_position_x}
|
||||
name="header_position_x"
|
||||
class="admin-range"
|
||||
/>
|
||||
</form>
|
||||
<form
|
||||
phx-change={@event_prefix <> "update_setting"}
|
||||
phx-value-field="header_position_y"
|
||||
>
|
||||
<div class="theme-slider-header">
|
||||
<span class="theme-slider-label">Vertical position</span>
|
||||
<span class="theme-slider-value">{@theme_settings.header_position_y}%</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={@theme_settings.header_position_y}
|
||||
name="header_position_y"
|
||||
class="admin-range"
|
||||
/>
|
||||
</form>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= for entry <- @uploads.theme_header_upload.entries do %>
|
||||
<.upload_progress entry={entry} upload_name="theme_header_upload" />
|
||||
<%= for err <- upload_errors(@uploads.theme_header_upload, entry) do %>
|
||||
<p class="theme-error-text">{error_to_string(err)}</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= for err <- upload_errors(@uploads.theme_header_upload) do %>
|
||||
<p class="theme-error-text">{error_to_string(err)}</p>
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# Shared upload progress component
|
||||
attr :entry, :map, required: true
|
||||
attr :upload_name, :string, required: true
|
||||
|
||||
defp upload_progress(assigns) do
|
||||
~H"""
|
||||
<div class="theme-progress">
|
||||
<div class="theme-progress-bar">
|
||||
<div class="theme-progress-fill" style={"width: #{@entry.progress}%"}></div>
|
||||
</div>
|
||||
<span class="admin-text-secondary">{@entry.progress}%</span>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="theme_cancel_upload"
|
||||
phx-value-ref={@entry.ref}
|
||||
phx-value-upload={@upload_name}
|
||||
class="theme-upload-cancel"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp error_to_string(:too_large), do: "File is too large"
|
||||
defp error_to_string(:too_many_files), do: "Too many files"
|
||||
defp error_to_string(:not_accepted), do: "File type not accepted"
|
||||
defp error_to_string(err), do: inspect(err)
|
||||
|
||||
# ── Full Customise Accordion ───────────────────────────────────────
|
||||
# Advanced settings groups for admin theme page.
|
||||
|
||||
|
||||
@ -9,6 +9,8 @@ defmodule BerrypodWeb.Shop.Page do
|
||||
use BerrypodWeb, :live_view
|
||||
|
||||
alias BerrypodWeb.Shop.Pages
|
||||
alias Berrypod.{Media, Settings}
|
||||
alias Berrypod.Workers.FaviconGeneratorWorker
|
||||
|
||||
# Map live_action atoms to page handler modules
|
||||
@page_modules %{
|
||||
@ -35,14 +37,159 @@ defmodule BerrypodWeb.Shop.Page do
|
||||
@impl true
|
||||
def mount(_params, session, socket) do
|
||||
# Store session for pages that need it (orders, order_detail)
|
||||
{:ok, assign(socket, :_session, session)}
|
||||
socket = assign(socket, :_session, session)
|
||||
|
||||
# Configure uploads only for admin users (theme editor image uploads)
|
||||
socket =
|
||||
if socket.assigns[:is_admin] do
|
||||
socket
|
||||
|> allow_upload(:theme_logo_upload,
|
||||
accept: ~w(.png .jpg .jpeg .webp .svg),
|
||||
max_entries: 1,
|
||||
max_file_size: 2_000_000,
|
||||
auto_upload: true,
|
||||
progress: &handle_theme_upload_progress/3
|
||||
)
|
||||
|> allow_upload(:theme_header_upload,
|
||||
accept: ~w(.png .jpg .jpeg .webp),
|
||||
max_entries: 1,
|
||||
max_file_size: 5_000_000,
|
||||
auto_upload: true,
|
||||
progress: &handle_theme_upload_progress/3
|
||||
)
|
||||
|> allow_upload(:theme_icon_upload,
|
||||
accept: ~w(.png .jpg .jpeg .webp .svg),
|
||||
max_entries: 1,
|
||||
max_file_size: 5_000_000,
|
||||
auto_upload: true,
|
||||
progress: &handle_theme_upload_progress/3
|
||||
)
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
# Handle theme image upload progress (logo, header, icon)
|
||||
defp handle_theme_upload_progress(:theme_logo_upload, entry, socket) do
|
||||
if entry.done? do
|
||||
consume_uploaded_entries(socket, :theme_logo_upload, fn %{path: path}, entry ->
|
||||
case Media.upload_from_entry(path, entry, "logo") do
|
||||
{:ok, image} ->
|
||||
Settings.update_theme_settings(%{logo_image_id: image.id})
|
||||
{:ok, image}
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
end)
|
||||
|> case do
|
||||
[image | _] ->
|
||||
# Trigger favicon generation if using logo as icon
|
||||
if socket.assigns[:theme_editor_settings] &&
|
||||
socket.assigns.theme_editor_settings.use_logo_as_icon do
|
||||
enqueue_favicon_generation(image.id)
|
||||
end
|
||||
|
||||
{:noreply,
|
||||
assign(socket, :theme_editor_logo_image, image) |> assign(:logo_image, image)}
|
||||
|
||||
_ ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
else
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_theme_upload_progress(:theme_header_upload, entry, socket) do
|
||||
if entry.done? do
|
||||
consume_uploaded_entries(socket, :theme_header_upload, fn %{path: path}, entry ->
|
||||
case Media.upload_from_entry(path, entry, "header") do
|
||||
{:ok, image} ->
|
||||
Settings.update_theme_settings(%{header_image_id: image.id})
|
||||
{:ok, image}
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
end)
|
||||
|> case do
|
||||
[image | _] ->
|
||||
socket =
|
||||
socket
|
||||
|> assign(:theme_editor_header_image, image)
|
||||
|> assign(:header_image, image)
|
||||
|> recompute_header_contrast()
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
_ ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
else
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_theme_upload_progress(:theme_icon_upload, entry, socket) do
|
||||
if entry.done? do
|
||||
consume_uploaded_entries(socket, :theme_icon_upload, fn %{path: path}, entry ->
|
||||
case Media.upload_from_entry(path, entry, "icon") do
|
||||
{:ok, image} ->
|
||||
Settings.update_theme_settings(%{icon_image_id: image.id})
|
||||
{:ok, image}
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
end)
|
||||
|> case do
|
||||
[image | _] ->
|
||||
enqueue_favicon_generation(image.id)
|
||||
|
||||
{:noreply,
|
||||
assign(socket, :theme_editor_icon_image, image) |> assign(:icon_image, image)}
|
||||
|
||||
_ ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
else
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
defp enqueue_favicon_generation(source_image_id) do
|
||||
%{source_image_id: source_image_id}
|
||||
|> FaviconGeneratorWorker.new()
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
||||
defp recompute_header_contrast(socket) do
|
||||
header_image = socket.assigns[:theme_editor_header_image]
|
||||
theme_settings = socket.assigns[:theme_editor_settings]
|
||||
|
||||
warning =
|
||||
if theme_settings && theme_settings.header_background_enabled && header_image do
|
||||
text_color = Berrypod.Theme.Contrast.text_color_for_mood(theme_settings.mood)
|
||||
colors = Berrypod.Theme.Contrast.parse_dominant_colors(header_image.dominant_colors)
|
||||
Berrypod.Theme.Contrast.analyze_header_contrast(colors, text_color)
|
||||
else
|
||||
:ok
|
||||
end
|
||||
|
||||
assign(socket, :theme_editor_contrast_warning, warning)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_params(params, uri, socket) do
|
||||
action = socket.assigns.live_action
|
||||
prev_action = socket.assigns[:_current_page_action]
|
||||
prev_path = socket.assigns[:_current_path]
|
||||
module = @page_modules[action]
|
||||
parsed_uri = URI.parse(uri)
|
||||
current_path = parsed_uri.path
|
||||
|
||||
# Clean up previous page if needed (e.g., unsubscribe from PubSub)
|
||||
socket = maybe_cleanup_previous_page(socket, prev_action)
|
||||
@ -73,6 +220,16 @@ defmodule BerrypodWeb.Shop.Page do
|
||||
# After page init, sync editor state if editing and page changed
|
||||
socket = maybe_sync_editing_blocks(socket)
|
||||
|
||||
# Scroll to top on navigation (different path, not just query param changes)
|
||||
socket =
|
||||
if prev_path && prev_path != current_path do
|
||||
Phoenix.LiveView.push_event(socket, "scroll-top", %{})
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
socket = assign(socket, :_current_path, current_path)
|
||||
|
||||
# Always call handle_params for URL changes
|
||||
case module.handle_params(params, uri, socket) do
|
||||
{:noreply, socket} -> {:noreply, socket}
|
||||
|
||||
@ -740,6 +740,15 @@ defmodule BerrypodWeb.PageEditorHook do
|
||||
{:halt, socket}
|
||||
end
|
||||
|
||||
defp handle_theme_action(
|
||||
"cancel_upload",
|
||||
%{"ref" => ref, "upload" => upload_name},
|
||||
socket
|
||||
) do
|
||||
upload_atom = String.to_existing_atom(upload_name)
|
||||
{:halt, Phoenix.LiveView.cancel_upload(socket, upload_atom, ref)}
|
||||
end
|
||||
|
||||
# Catch-all for unknown theme actions
|
||||
defp handle_theme_action(_action, _params, socket), do: {:halt, socket}
|
||||
|
||||
|
||||
@ -111,6 +111,11 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
theme_editor_active_preset={Map.get(assigns, :theme_editor_active_preset)}
|
||||
theme_editor_presets={Map.get(assigns, :theme_editor_presets, [])}
|
||||
theme_editor_customise_open={Map.get(assigns, :theme_editor_customise_open, false)}
|
||||
theme_editor_logo_image={Map.get(assigns, :theme_editor_logo_image)}
|
||||
theme_editor_header_image={Map.get(assigns, :theme_editor_header_image)}
|
||||
theme_editor_icon_image={Map.get(assigns, :theme_editor_icon_image)}
|
||||
theme_editor_contrast_warning={Map.get(assigns, :theme_editor_contrast_warning, :ok)}
|
||||
uploads={Map.get(assigns, :uploads)}
|
||||
site_name={Map.get(assigns, :site_name, "")}
|
||||
product={assigns[:product]}
|
||||
collection_title={assigns[:collection_title]}
|
||||
@ -143,6 +148,11 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
attr :theme_editor_active_preset, :atom, default: nil
|
||||
attr :theme_editor_presets, :list, default: []
|
||||
attr :theme_editor_customise_open, :boolean, default: false
|
||||
attr :theme_editor_logo_image, :map, default: nil
|
||||
attr :theme_editor_header_image, :map, default: nil
|
||||
attr :theme_editor_icon_image, :map, default: nil
|
||||
attr :theme_editor_contrast_warning, :atom, default: :ok
|
||||
attr :uploads, :map, default: nil
|
||||
attr :site_name, :string, default: ""
|
||||
attr :product, :map, default: nil
|
||||
attr :collection_title, :string, default: nil
|
||||
@ -179,6 +189,11 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
theme_editor_active_preset={@theme_editor_active_preset}
|
||||
theme_editor_presets={@theme_editor_presets}
|
||||
theme_editor_customise_open={@theme_editor_customise_open}
|
||||
theme_editor_logo_image={@theme_editor_logo_image}
|
||||
theme_editor_header_image={@theme_editor_header_image}
|
||||
theme_editor_icon_image={@theme_editor_icon_image}
|
||||
theme_editor_contrast_warning={@theme_editor_contrast_warning}
|
||||
uploads={@uploads}
|
||||
site_name={@site_name}
|
||||
/>
|
||||
"""
|
||||
@ -203,6 +218,11 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
attr :theme_editor_active_preset, :atom, default: nil
|
||||
attr :theme_editor_presets, :list, default: []
|
||||
attr :theme_editor_customise_open, :boolean, default: false
|
||||
attr :theme_editor_logo_image, :map, default: nil
|
||||
attr :theme_editor_header_image, :map, default: nil
|
||||
attr :theme_editor_icon_image, :map, default: nil
|
||||
attr :theme_editor_contrast_warning, :atom, default: :ok
|
||||
attr :uploads, :map, default: nil
|
||||
attr :site_name, :string, default: ""
|
||||
|
||||
defp theme_editor_content(assigns) do
|
||||
@ -213,6 +233,11 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
presets={@theme_editor_presets}
|
||||
site_name={@site_name}
|
||||
customise_open={@theme_editor_customise_open}
|
||||
uploads={@uploads}
|
||||
logo_image={@theme_editor_logo_image}
|
||||
header_image={@theme_editor_header_image}
|
||||
icon_image={@theme_editor_icon_image}
|
||||
contrast_warning={@theme_editor_contrast_warning}
|
||||
event_prefix="theme_"
|
||||
/>
|
||||
"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user