Packages
phoenix_kit
1.7.113
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit_web/components/media_gallery.html.heex
<div id={@id}>
<div class="flex flex-col gap-2">
<%!-- Optional heading --%>
<div :if={@title} class="flex items-center gap-2">
<span class="text-xs font-medium text-base-content/60">{@title}</span>
</div>
<%!-- Thumbnail grid via the canonical <.draggable_list> primitive.
Grid layout (cols configurable), aspect-square thumbnails, featured
badge on the first item (opt-in), hover-revealed remove + preview
buttons. Add-tile sits as the trailing grid cell so the picker is
visually part of the gallery. --%>
<.draggable_list
id={"#{@id}-grid"}
items={@selected}
item_id={& &1}
on_reorder="reorder_images"
target={"##{@id}"}
cols={@cols}
draggable={not @readonly and length(@selected) > 1}
>
<:item :let={uuid}>
<div class="relative group aspect-square">
<.image_set
file_uuid={uuid}
variants={Map.get(@variants_map, uuid, [])}
alt=""
class="w-full h-full object-cover rounded-lg border-2 border-base-300"
loading="eager"
/>
<%!-- Featured badge: only the first item in @selected, only when
the consumer opted in via featured_first={true}. --%>
<div
:if={@featured_first and uuid == List.first(@selected)}
class="absolute top-1 left-1 badge badge-primary badge-xs h-auto pointer-events-none"
>
{gettext("Featured")}
</div>
<%!-- Remove button (hover-revealed, top-right) — hidden in readonly. --%>
<button
:if={not @readonly}
type="button"
class="sortable-ignore absolute top-1 right-1 btn btn-xs btn-circle btn-error opacity-0 group-hover:opacity-100 transition-opacity"
data-role={"remove-image-#{@id}-#{uuid}"}
phx-click="remove_image"
phx-value-uuid={uuid}
phx-target={@myself}
title={gettext("Remove")}
>
<.icon name="hero-x-mark" class="w-3 h-3" />
</button>
<%!-- Eye / preview button (hover-revealed, bottom-right). Available
in readonly mode too — lightbox is a view-only feature. --%>
<button
type="button"
class="sortable-ignore absolute bottom-1 right-1 btn btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity"
data-role={"preview-image-#{@id}-#{uuid}"}
phx-click="preview_image"
phx-value-uuid={uuid}
phx-target={@myself}
title={gettext("Preview")}
>
<.icon name="hero-eye" class="w-3 h-3" />
</button>
</div>
</:item>
<:add_button>
<button
:if={not @readonly}
type="button"
class="w-full aspect-square border-2 border-dashed border-base-300 rounded-lg hover:border-primary hover:bg-base-200 transition-all cursor-pointer flex flex-col items-center justify-center gap-1 text-base-content/60 hover:text-primary"
data-role={"open-picker-#{@id}"}
phx-click="open_picker"
phx-target={@myself}
>
<.icon name="hero-plus" class="w-6 h-6" />
<span class="text-xs font-medium">{gettext("Add")}</span>
</button>
</:add_button>
</.draggable_list>
<%!-- Media selector modal (embedded, self-contained). Only mounted once
the user clicks the Add tile — this avoids rendering a live_component
node in static/test contexts where `show_picker` is false and no
LiveView process exists. --%>
<.live_component
:if={not @readonly and @show_picker}
module={MediaSelectorModal}
id={"#{@id}-media-selector"}
show={true}
mode={@mode}
selected_uuids={@selected}
file_type_filter={:image}
scope_folder_id={@scope_folder_id}
phoenix_kit_current_user={@phoenix_kit_current_user}
notify={{__MODULE__, @id}}
/>
</div>
<%!-- Lightbox — delegated to MediaViewer, inside the single root so morphdom patches it reliably --%>
<.live_component
:if={@preview_uuid}
module={PhoenixKitWeb.Components.MediaViewer}
id={"#{@id}-viewer"}
files={@selected}
current={@preview_uuid}
current_user={@phoenix_kit_current_user}
notify={{__MODULE__, @id}}
/>
</div>