Created
October 2, 2023 10:20
-
-
Save dankentfield/719666ede4370ab7a9a6c511a82b0983 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Application.put_env(:sample, Example.Endpoint, | |
http: [ip: {127, 0, 0, 1}, port: 5001], | |
server: true, | |
live_view: [signing_salt: "aaaaaaaa"], | |
secret_key_base: String.duplicate("a", 64), | |
pubsub_server: Example.PubSub | |
) | |
Mix.install([ | |
{:plug_cowboy, "~> 2.5"}, | |
{:jason, "~> 1.0"}, | |
{:phoenix, "~> 1.7", override: true}, | |
{:phoenix_live_view, "~> 0.20"} | |
]) | |
defmodule Example.ErrorView do | |
def render(template, _), do: Phoenix.Controller.status_message_from_template(template) | |
end | |
defmodule Example.HomeLive do | |
use Phoenix.LiveView, layout: {__MODULE__, :live} | |
import Phoenix.Component | |
def render("live.html", assigns) do | |
~H""" | |
<style>body{margin: 5rem 10rem}blockquote,pre>code{padding:1rem 1.5rem}dl,ol,p,ul{margin-top:0}.row .column,img{max-width:100%}#media-recorder-demo,html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}html{font-size:62.5%}body{color:#000;font-family:Helvetica,Arial,sans-serif;font-size:1.6em;font-weight:300;line-height:1.6}b,label,legend,strong{font-weight:700}blockquote{border-left:.3rem solid #d1d1d1;margin-left:0;margin-right:0}.alert p,blockquote :last-child{margin-bottom:0}.button,button,input[type=button],input[type=reset],input[type=submit]{background-color:#0069d9;border:.1rem solid #0069d9;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}code,pre{background:#f4f5f6}dl,ol,td:first-child,th:first-child,ul{padding-left:0}.row,.row.row-no-padding,.row.row-no-padding>.column,fieldset{padding:0}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type=button][disabled]:focus,input[type=button][disabled]:hover,input[type=reset][disabled]:focus,input[type=reset][disabled]:hover,input[type=submit][disabled]:focus,input[type=submit][disabled]:hover{background-color:#0069d9;border-color:#0069d9}.button.button-outline,button.button-outline,input[type=button].button-outline,input[type=reset].button-outline,input[type=submit].button-outline{background-color:transparent;color:#0069d9}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type=button].button-outline:focus,input[type=button].button-outline:hover,input[type=reset].button-outline:focus,input[type=reset].button-outline:hover,input[type=submit].button-outline:focus,input[type=submit].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type=button].button-outline[disabled]:focus,input[type=button].button-outline[disabled]:hover,input[type=reset].button-outline[disabled]:focus,input[type=reset].button-outline[disabled]:hover,input[type=submit].button-outline[disabled]:focus,input[type=submit].button-outline[disabled]:hover{border-color:inherit;color:#0069d9}.button.button-clear,button.button-clear,input[type=button].button-clear,input[type=reset].button-clear,input[type=submit].button-clear{background-color:transparent;border-color:transparent;color:#0069d9}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type=button].button-clear:focus,input[type=button].button-clear:hover,input[type=reset].button-clear:focus,input[type=reset].button-clear:hover,input[type=submit].button-clear:focus,input[type=submit].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,a,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type=button].button-clear[disabled]:focus,input[type=button].button-clear[disabled]:hover,input[type=reset].button-clear[disabled]:focus,input[type=reset].button-clear[disabled]:hover,input[type=submit].button-clear[disabled]:focus,input[type=submit].button-clear[disabled]:hover{color:#0069d9}code{border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{border-left:.3rem solid #0069d9;overflow-y:hidden;padding:1em}pre>code{border-radius:0;display:block;white-space:pre}hr{border:0;border-top:.1rem solid #f4f5f6;margin:3rem 0}input[type=email],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=url],select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1rem;width:100%}.phx-logo img,select{width:auto}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=url]:focus,select:focus,textarea:focus{border-color:#0069d9;outline:0}select{background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="%23d1d1d1" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>') center right no-repeat;padding-right:3rem}select:focus{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="%230069d9" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;margin-bottom:.5rem}fieldset{border-width:0}input[type=checkbox],input[type=radio]{display:inline}.label-inline{display:inline-block;font-weight:400;margin-left:.5rem}.row{display:flex;flex-direction:column;width:100%}.phx-logo,.phx-logo img,.row .column,header nav a{display:block}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{flex:1 1 auto;margin-left:0;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width:40rem){.row{flex-direction:row;margin-left:-1rem;width:calc(100% + 2rem)}.row .column{margin-bottom:inherit;padding:0 1rem}}a{text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:last-child,th:last-child{padding-right:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2rem;margin-top:0}h3,h4{letter-spacing:-.08rem}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}h1{font-size:3.6rem;line-height:1.25}h2{font-size:2.8rem;line-height:1.3}h3{font-size:2.2rem;line-height:1.35}h4{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h5{letter-spacing:-.05rem;font-size:1.6rem;letter-spacing:0;line-height:1.4}h6{font-size:1.4rem;letter-spacing:0;line-height:1.2}.container{margin:0 auto;max-width:80rem;padding:0 2rem;position:relative;width:100%}.phx-hero{text-align:center;border-bottom:1px solid #e3e3e3;background:#eee;border-radius:6px;padding:3em 3em 1em;margin-bottom:3rem;font-weight:200;font-size:120%}.phx-logo,.upload-component>.preview img{margin:1rem}.phx-hero input{background:#fff}.phx-logo{min-width:300px}header{width:100%;background:#fdfdfd;border-bottom:1px solid #eaeaea;margin-bottom:2rem}header section{align-items:center;display:flex;flex-direction:column;justify-content:space-between}header section :first-child{order:2}header section :last-child{order:1}header nav li,header nav ul{margin:0;padding:0;display:block;text-align:right;white-space:nowrap}header nav ul{margin:0 1rem 1rem}@media (min-width:40.0rem){header section{flex-direction:row}header nav ul{margin:1rem}.phx-logo{flex-basis:527px;margin:2rem 1rem}}details{margin:1rem 0 2rem}details:not([open]) summary::after{content:" (click to expand)"}.upload-demo{margin-bottom:2rem}.upload-entry{border-bottom:1px dashed #ccc;padding:.87rem 0}.upload-entry__progress{height:.87rem;width:100%;min-width:100%}.upload-entry__details{display:flex;padding-bottom:1rem}.upload-entry__details figure{display:flex;flex-basis:100%;margin:1rem 1.5rem 0 0}.upload-entry__details figure img{width:96px;object-fit:contain;align-self:flex-start;padding-right:.87rem}.upload-entry__details figure figcaption{flex:1 1 auto;align-self:center}.avatar>img,output>figure img{margin:1rem 1.5rem 0 0}output>figure figcaption{font-family:monospace;font-size:smaller}#media-recorder-demo .button{border-color:#f26f40}#media-recorder-demo .button-outline{font-size:2rem;transition:.4s;cursor:pointer;color:#f26f40}#media-recorder-demo .button-outline:focus,#media-recorder-demo .button-outline:hover{background:#f26f40;color:#fff}.alert:empty,.phx-no-feedback .invalid-feedback,.phx-no-feedback.invalid-feedback{display:none}.phx-click-loading{opacity:.5;transition:opacity 1s ease-out}.phx-disconnected{cursor:wait}.phx-disconnected *{pointer-events:none}.phx-modal{opacity:1!important;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgba(0,0,0,.4)}.phx-modal-content{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%}.phx-modal-close{color:#aaa;float:right;font-size:28px;font-weight:700}.phx-modal-close:focus,.phx-modal-close:hover{color:#000;text-decoration:none;cursor:pointer}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.invalid-feedback{color:#a94442;display:block;margin:-1rem 0 2rem}.visually-hidden{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}</style> | |
<script src="https://cdn.jsdelivr.net/npm/phoenix@1.7.2/priv/static/phoenix.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/phoenix_live_view@0.20.0/priv/static/phoenix_live_view.min.js"></script> | |
<script> | |
let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket) | |
liveSocket.connect() | |
</script> | |
<%= @inner_content %> | |
""" | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Automatic Uploads Example</h1> | |
<p> | |
Uploads begin automatically. In this example, the <code>chunk_size</code> | |
has been set very low so you can more easily observe the progress state changes. | |
</p> | |
<section class="row upload-demo"> | |
<section class="column"> | |
<h2>Upload</h2> | |
<p>You may add up to <%= @uploads.exhibit.max_entries %> exhibits at a time.</p> | |
<%= for error <- upload_errors(@uploads.exhibit) do %> | |
<p class="alert alert-danger"><%= upload_error_to_string(error) %></p> | |
<% end %> | |
<form id="auto-form" phx-change="validate"> | |
<.live_file_input upload={@uploads.exhibit} /> | |
</form> | |
<section | |
class="pending-uploads" | |
phx-drop-target={@uploads.exhibit.ref} | |
style="min-height: 100%;" | |
> | |
<h3>Pending Uploads (<%= length(@uploads.exhibit.entries) %>)</h3> | |
<%= for entry <- @uploads[:exhibit].entries do %> | |
<div> | |
<div> | |
<%= entry.uuid %><br /> | |
<a | |
href="#" | |
phx-click="cancel-upload" | |
phx-value-ref={entry.ref} | |
class="upload-entry__cancel" | |
> | |
Cancel Upload | |
</a> | |
<%= for error <- upload_errors(@uploads.exhibit, entry) do %> | |
<p class="alert alert-danger"><%= upload_error_to_string(error) %></p> | |
<% end %> | |
</div> | |
</div> | |
<% end %> | |
</section> | |
</section> | |
<section class="column"> | |
<h2>Uploaded Files (<%= length(@uploaded_files) %>)</h2> | |
<.figure_group paths={@uploaded_files} /> | |
</section> | |
</section> | |
""" | |
end | |
def mount(_params, _session, socket) do | |
{:ok, | |
socket | |
|> assign(:uploaded_files, []) | |
|> allow_upload(:exhibit, | |
accept: :any, | |
max_entries: 2, | |
max_file_size: 100 * 1_024 * 1_024, | |
chunk_size: 256, | |
auto_upload: true, | |
progress: &handle_progress/3 | |
)} | |
end | |
# with auto_upload: true we can consume files here | |
defp handle_progress(:exhibit, entry, socket) do | |
if entry.done? do | |
uuid = | |
consume_uploaded_entry(socket, entry, fn _meta -> | |
{:ok, entry.uuid} | |
end) | |
{:noreply, update(socket, :uploaded_files, &[uuid | &1])} | |
else | |
{:noreply, socket} | |
end | |
end | |
def handle_event("validate", _params, socket) do | |
{:noreply, socket} | |
end | |
def handle_event("cancel-upload", %{"ref" => ref}, socket) do | |
{:noreply, cancel_upload(socket, :exhibit, ref)} | |
end | |
attr :paths, :list, required: true, doc: "A list of URI paths" | |
def figure_group(assigns), do: ~H""" | |
<div data-figure-group> | |
<.figure :for={path <- @paths} src={path} caption={path} /> | |
</div> | |
""" | |
attr :src, :string, required: true, doc: "The absolute URI path to the image" | |
attr :caption, :string, default: "" | |
slot :inner_block | |
def figure(assigns), do: ~H""" | |
<figure> | |
<img src={@src} /> | |
<figcaption :if={@caption != ""}><%= @caption %></figcaption> | |
</figure> | |
""" | |
@doc """ | |
Returns an error message for an upload error. | |
See [`upload_errors/2`](`Phoenix.Component.upload_errors/2`). | |
""" | |
def upload_error_to_string(:too_large), do: "The file is too large" | |
def upload_error_to_string(:too_many_files), do: "You have selected too many files" | |
def upload_error_to_string(:not_accepted), do: "You have selected an unacceptable file type" | |
def upload_error_to_string(:external_client_failure), do: "Something went terribly wrong" | |
end | |
defmodule Example.Router do | |
use Phoenix.Router | |
import Phoenix.LiveView.Router | |
pipeline :browser do | |
plug(:accepts, ["html"]) | |
end | |
scope "/", Example do | |
pipe_through(:browser) | |
live("/", HomeLive, :index) | |
end | |
end | |
defmodule Example.Endpoint do | |
use Phoenix.Endpoint, otp_app: :sample | |
socket("/live", Phoenix.LiveView.Socket) | |
plug(Example.Router) | |
end | |
{:ok, _} = Supervisor.start_link([Example.Endpoint, | |
{Phoenix.PubSub, name: Example.PubSub}, | |
], strategy: :one_for_one) | |
Process.sleep(:infinity) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment