diff --git a/demo/.formatter.exs b/demo/.formatter.exs index cd4b3bc6..7f08245b 100644 --- a/demo/.formatter.exs +++ b/demo/.formatter.exs @@ -1,5 +1,5 @@ [ import_deps: [:phoenix], - inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"], - line_length: 80 + plugins: [Phoenix.LiveView.HTMLFormatter], + inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "storybook/**/*.exs"] ] diff --git a/demo/.gitignore b/demo/.gitignore index 085507ed..2f07cea1 100644 --- a/demo/.gitignore +++ b/demo/.gitignore @@ -19,6 +19,9 @@ erl_crash.dump # Also ignore archive artifacts (built via "mix archive.build"). *.ez +# Temporary files, for example, from tests. +/tmp/ + # Ignore package tarball (built via "mix hex.build"). demo-*.tar diff --git a/demo/README.md b/demo/README.md index 1cd5e80d..5c399184 100644 --- a/demo/README.md +++ b/demo/README.md @@ -3,6 +3,7 @@ To start your Phoenix server: - Install dependencies with `mix deps.get` -- Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server` +- Start Phoenix endpoint with `mix phx.server` or inside IEx with + `iex -S mix phx.server` Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. diff --git a/demo/assets/css/app.css b/demo/assets/css/app.css index 25aa8024..30f36f3a 100644 --- a/demo/assets/css/app.css +++ b/demo/assets/css/app.css @@ -1,151 +1,645 @@ -/* This file is for your main application CSS */ -@import "./phoenix.css"; +/*! + * Milligram v1.4.1 + * https://milligram.io + * + * Copyright (c) 2020 CJ Patoilo + * Licensed under the MIT license + */ -/* Alerts and form errors used by phx.new */ -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; +*, +*:after, +*:before { + box-sizing: inherit; } -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; +html { + box-sizing: border-box; + font-size: 62.5%; } -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; +body { + color: #606c76; + font-family: "Roboto", "Helvetica Neue", "Helvetica", "Arial", sans-serif; + font-size: 1.6em; + font-weight: 300; + letter-spacing: 0.01em; + line-height: 1.6; } -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; +blockquote { + border-left: 0.3rem solid #d1d1d1; + margin-left: 0; + margin-right: 0; + padding: 1rem 1.5rem; } -.alert p { +blockquote *:last-child { margin-bottom: 0; } -.alert:empty { - display: none; +.button, +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + background-color: #9b4dca; + border: 0.1rem solid #9b4dca; + border-radius: 0.4rem; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 1.1rem; + font-weight: 700; + height: 3.8rem; + letter-spacing: 0.1rem; + line-height: 3.8rem; + padding: 0 3rem; + text-align: center; + text-decoration: none; + text-transform: uppercase; + white-space: nowrap; +} + +.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: 0.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: #9b4dca; + border-color: #9b4dca; +} + +.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: #9b4dca; +} + +.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: #9b4dca; +} + +.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: #9b4dca; +} + +.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, +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: #9b4dca; } -.invalid-feedback { - color: #a94442; +code { + background: #f4f5f6; + border-radius: 0.4rem; + font-size: 86%; + margin: 0 0.2rem; + padding: 0.2rem 0.5rem; + white-space: nowrap; +} + +pre { + background: #f4f5f6; + border-left: 0.3rem solid #9b4dca; + overflow-y: hidden; +} + +pre > code { + border-radius: 0; display: block; - margin: -1rem 0 2rem; + padding: 1rem 1.5rem; + white-space: pre; } -/* LiveView specific classes for your customization */ -.phx-no-feedback.invalid-feedback, -.phx-no-feedback .invalid-feedback { - display: none; +hr { + border: 0; + border-top: 0.1rem solid #f4f5f6; + margin: 3rem 0; } -.phx-click-loading { - opacity: 0.5; - transition: opacity 1s ease-out; +input[type="color"], +input[type="date"], +input[type="datetime"], +input[type="datetime-local"], +input[type="email"], +input[type="month"], +input[type="number"], +input[type="password"], +input[type="search"], +input[type="tel"], +input[type="text"], +input[type="url"], +input[type="week"], +input:not([type]), +textarea, +select { + -webkit-appearance: none; + background-color: transparent; + border: 0.1rem solid #d1d1d1; + border-radius: 0.4rem; + box-shadow: none; + box-sizing: inherit; + height: 3.8rem; + padding: 0.6rem 1rem 0.7rem; + width: 100%; +} + +input[type="color"]:focus, +input[type="date"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="email"]:focus, +input[type="month"]: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, +input[type="week"]:focus, +input:not([type]):focus, +textarea:focus, +select:focus { + border-color: #9b4dca; + outline: 0; +} + +select { + background: url('data:image/svg+xml;utf8,') + center right no-repeat; + padding-right: 3rem; +} + +select:focus { + background-image: url('data:image/svg+xml;utf8,'); +} + +select[multiple] { + background: none; + height: auto; +} + +textarea { + min-height: 6.5rem; +} + +label, +legend { + display: block; + font-size: 1.6rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +fieldset { + border-width: 0; + padding: 0; +} + +input[type="checkbox"], +input[type="radio"] { + display: inline; } -.phx-loading { - cursor: wait; +.label-inline { + display: inline-block; + font-weight: normal; + margin-left: 0.5rem; } -.phx-modal { - opacity: 1 !important; - position: fixed; - z-index: 1; - left: 0; - top: 0; +.container { + margin: 0 auto; + max-width: 112rem; + padding: 0 2rem; + position: relative; width: 100%; - height: 100%; - overflow: auto; - background-color: rgba(0, 0, 0, 0.4); } -.phx-modal-content { - background-color: #fefefe; - margin: 15vh auto; - padding: 20px; - border: 1px solid #888; - width: 80%; +.row { + display: flex; + flex-direction: column; + padding: 0; + width: 100%; } -.phx-modal-close { - color: #aaa; - float: right; - font-size: 28px; - font-weight: bold; +.row.row-no-padding { + padding: 0; } -.phx-modal-close:hover, -.phx-modal-close:focus { - color: black; - text-decoration: none; - cursor: pointer; +.row.row-no-padding > .column { + padding: 0; } -.fade-in-scale { - animation: 0.2s ease-in 0s normal forwards 1 fade-in-scale-keys; +.row.row-wrap { + flex-wrap: wrap; } -.fade-out-scale { - animation: 0.2s ease-out 0s normal forwards 1 fade-out-scale-keys; +.row.row-top { + align-items: flex-start; } -.fade-in { - animation: 0.2s ease-out 0s normal forwards 1 fade-in-keys; +.row.row-bottom { + align-items: flex-end; } -.fade-out { - animation: 0.2s ease-out 0s normal forwards 1 fade-out-keys; +.row.row-center { + align-items: center; } -@keyframes fade-in-scale-keys { - 0% { - scale: 0.95; - opacity: 0; - } +.row.row-stretch { + align-items: stretch; +} - 100% { - scale: 1; - opacity: 1; - } +.row.row-baseline { + align-items: baseline; } -@keyframes fade-out-scale-keys { - 0% { - scale: 1; - opacity: 1; - } +.row .column { + display: block; + flex: 1 1 auto; + margin-left: 0; + max-width: 100%; + width: 100%; +} - 100% { - scale: 0.95; - opacity: 0; - } +.row .column.column-offset-10 { + margin-left: 10%; } -@keyframes fade-in-keys { - 0% { - opacity: 0; - } +.row .column.column-offset-20 { + margin-left: 20%; +} - 100% { - opacity: 1; - } +.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-40 { + margin-left: 40%; +} + +.row .column.column-offset-50 { + margin-left: 50%; +} + +.row .column.column-offset-60 { + margin-left: 60%; +} + +.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%; } -@keyframes fade-out-keys { - 0% { - opacity: 1; +.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 { + 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 { + color: #9b4dca; + text-decoration: none; +} + +a:focus, +a:hover { + color: #606c76; +} + +dl, +ol, +ul { + list-style: none; + margin-top: 0; + padding-left: 0; +} + +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; +} - 100% { - opacity: 0; +blockquote, +dl, +figure, +form, +ol, +p, +pre, +table, +ul { + margin-bottom: 2.5rem; +} + +table { + border-spacing: 0; + display: block; + overflow-x: auto; + text-align: left; + width: 100%; +} + +td, +th { + border-bottom: 0.1rem solid #e1e1e1; + padding: 1.2rem 1.5rem; +} + +td:first-child, +th:first-child { + padding-left: 0; +} + +td:last-child, +th:last-child { + padding-right: 0; +} + +@media (min-width: 40rem) { + table { + display: table; + overflow-x: initial; } } + +b, +strong { + font-weight: bold; +} + +p { + margin-top: 0; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 300; + letter-spacing: -0.1rem; + margin-bottom: 2rem; + margin-top: 0; +} + +h1 { + font-size: 4.6rem; + line-height: 1.2; +} + +h2 { + font-size: 3.6rem; + line-height: 1.25; +} + +h3 { + font-size: 2.8rem; + line-height: 1.3; +} + +h4 { + font-size: 2.2rem; + letter-spacing: -0.08rem; + line-height: 1.35; +} + +h5 { + font-size: 1.8rem; + letter-spacing: -0.05rem; + line-height: 1.5; +} + +h6 { + font-size: 1.6rem; + letter-spacing: 0; + line-height: 1.4; +} + +img { + max-width: 100%; +} + +.clearfix:after { + clear: both; + content: " "; + display: table; +} + +.float-left { + float: left; +} + +.float-right { + float: right; +} diff --git a/demo/assets/css/phoenix.css b/demo/assets/css/phoenix.css deleted file mode 100644 index 152f1651..00000000 --- a/demo/assets/css/phoenix.css +++ /dev/null @@ -1,781 +0,0 @@ -/* Includes some default style for the starter application. - * This can be safely deleted to start fresh. - */ - -/* Milligram v1.4.1 https://milligram.github.io - * Copyright (c) 2020 CJ Patoilo Licensed under the MIT license - */ - -*, -*:after, -*:before { - box-sizing: inherit; -} - -html { - box-sizing: border-box; - font-size: 62.5%; -} - -body { - color: #000000; - font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif; - font-size: 1.6em; - font-weight: 300; - letter-spacing: 0.01em; - line-height: 1.6; -} - -blockquote { - border-left: 0.3rem solid #d1d1d1; - margin-left: 0; - margin-right: 0; - padding: 1rem 1.5rem; -} - -blockquote *:last-child { - margin-bottom: 0; -} - -.button, -button, -input[type="button"], -input[type="reset"], -input[type="submit"] { - background-color: #0069d9; - border: 0.1rem solid #0069d9; - border-radius: 0.4rem; - color: #fff; - cursor: pointer; - display: inline-block; - font-size: 1.1rem; - font-weight: 700; - height: 3.8rem; - letter-spacing: 0.1rem; - line-height: 3.8rem; - padding: 0 3rem; - text-align: center; - text-decoration: none; - text-transform: uppercase; - white-space: nowrap; -} - -.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: 0.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, -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 { - background: #f4f5f6; - border-radius: 0.4rem; - font-size: 86%; - margin: 0 0.2rem; - padding: 0.2rem 0.5rem; - white-space: nowrap; -} - -pre { - background: #f4f5f6; - border-left: 0.3rem solid #0069d9; - overflow-y: hidden; -} - -pre > code { - border-radius: 0; - display: block; - padding: 1rem 1.5rem; - white-space: pre; -} - -hr { - border: 0; - border-top: 0.1rem solid #f4f5f6; - margin: 3rem 0; -} - -input[type="color"], -input[type="date"], -input[type="datetime"], -input[type="datetime-local"], -input[type="email"], -input[type="month"], -input[type="number"], -input[type="password"], -input[type="search"], -input[type="tel"], -input[type="text"], -input[type="url"], -input[type="week"], -input:not([type]), -textarea, -select { - -webkit-appearance: none; - background-color: transparent; - border: 0.1rem solid #d1d1d1; - border-radius: 0.4rem; - box-shadow: none; - box-sizing: inherit; - height: 3.8rem; - padding: 0.6rem 1rem 0.7rem; - width: 100%; -} - -input[type="color"]:focus, -input[type="date"]:focus, -input[type="datetime"]:focus, -input[type="datetime-local"]:focus, -input[type="email"]:focus, -input[type="month"]: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, -input[type="week"]:focus, -input:not([type]):focus, -textarea:focus, -select:focus { - border-color: #0069d9; - outline: 0; -} - -select { - background: url('data:image/svg+xml;utf8,') - center right no-repeat; - padding-right: 3rem; -} - -select:focus { - background-image: url('data:image/svg+xml;utf8,'); -} - -select[multiple] { - background: none; - height: auto; -} - -textarea { - min-height: 6.5rem; -} - -label, -legend { - display: block; - font-size: 1.6rem; - font-weight: 700; - margin-bottom: 0.5rem; -} - -fieldset { - border-width: 0; - padding: 0; -} - -input[type="checkbox"], -input[type="radio"] { - display: inline; -} - -.label-inline { - display: inline-block; - font-weight: normal; - margin-left: 0.5rem; -} - -.container { - margin: 0 auto; - max-width: 112rem; - padding: 0 2rem; - position: relative; - width: 100%; -} - -.row { - display: flex; - flex-direction: column; - padding: 0; - width: 100%; -} - -.row.row-no-padding { - padding: 0; -} - -.row.row-no-padding > .column { - padding: 0; -} - -.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 { - display: block; - flex: 1 1 auto; - margin-left: 0; - max-width: 100%; - 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-40 { - margin-left: 40%; -} - -.row .column.column-offset-50 { - margin-left: 50%; -} - -.row .column.column-offset-60 { - margin-left: 60%; -} - -.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 { - 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 { - color: #0069d9; - text-decoration: none; -} - -a:focus, -a:hover { - color: #606c76; -} - -dl, -ol, -ul { - list-style: none; - margin-top: 0; - padding-left: 0; -} - -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; - display: block; - overflow-x: auto; - text-align: left; - width: 100%; -} - -td, -th { - border-bottom: 0.1rem solid #e1e1e1; - padding: 1.2rem 1.5rem; -} - -td:first-child, -th:first-child { - padding-left: 0; -} - -td:last-child, -th:last-child { - padding-right: 0; -} - -@media (min-width: 40rem) { - table { - display: table; - overflow-x: initial; - } -} - -b, -strong { - font-weight: bold; -} - -p { - margin-top: 0; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 300; - letter-spacing: -0.1rem; - margin-bottom: 2rem; - margin-top: 0; -} - -h1 { - font-size: 4.6rem; - line-height: 1.2; -} - -h2 { - font-size: 3.6rem; - line-height: 1.25; -} - -h3 { - font-size: 2.8rem; - line-height: 1.3; -} - -h4 { - font-size: 2.2rem; - letter-spacing: -0.08rem; - line-height: 1.35; -} - -h5 { - font-size: 1.8rem; - letter-spacing: -0.05rem; - line-height: 1.5; -} - -h6 { - font-size: 1.6rem; - letter-spacing: 0; - line-height: 1.4; -} - -img { - max-width: 100%; -} - -.clearfix:after { - clear: both; - content: " "; - display: table; -} - -.float-left { - float: left; -} - -.float-right { - float: right; -} - -/* General style */ -h1 { - font-size: 3.6rem; - line-height: 1.25; -} - -h2 { - font-size: 2.8rem; - line-height: 1.3; -} - -h3 { - font-size: 2.2rem; - letter-spacing: -0.08rem; - line-height: 1.35; -} - -h4 { - font-size: 1.8rem; - letter-spacing: -0.05rem; - line-height: 1.5; -} - -h5 { - font-size: 1.6rem; - letter-spacing: 0; - line-height: 1.4; -} - -h6 { - font-size: 1.4rem; - letter-spacing: 0; - line-height: 1.2; -} - -pre { - padding: 1em; -} - -.container { - margin: 0 auto; - max-width: 80rem; - padding: 0 2rem; - position: relative; - width: 100%; -} - -select { - width: auto; -} - -/* Phoenix promo and logo */ -.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-hero input { - background: #ffffff; -} - -.phx-logo { - min-width: 300px; - margin: 1rem; - display: block; -} - -.phx-logo img { - width: auto; - display: block; -} - -/* Headers */ -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 ul, -header nav li { - margin: 0; - padding: 0; - display: block; - text-align: right; - white-space: nowrap; -} - -header nav ul { - margin: 1rem; - margin-top: 0; -} - -header nav a { - display: block; -} - -@media (min-width: 40rem) { - /* Small devices (landscape phones, 576px and up) */ - header section { - flex-direction: row; - } - - header nav ul { - margin: 1rem; - } - - .phx-logo { - flex-basis: 527px; - margin: 2rem 1rem; - } -} diff --git a/demo/assets/css/storybook.css b/demo/assets/css/storybook.css index 2d2ac9bf..48f1098e 100644 --- a/demo/assets/css/storybook.css +++ b/demo/assets/css/storybook.css @@ -1,9 +1,3 @@ -/* - * This is your custom storybook stylesheet. - * Put your component styling under ."demo-web" scope. - * See the https://hexdocs.pm/phx_live_storybook/sandboxing.html guide for more info. - */ - .demo-web { font-family: ui-sans-serif, system-ui; } diff --git a/demo/assets/js/app.js b/demo/assets/js/app.js index cf24fa49..2b351dc1 100644 --- a/demo/assets/js/app.js +++ b/demo/assets/js/app.js @@ -1,7 +1,3 @@ -// We import the CSS which is extracted to its own file by esbuild. -// Remove this line if you add a your own CSS build pipeline (e.g postcss). -import "../css/app.css"; - // If you want to use Phoenix channels, run `mix help phx.gen.channel` // to get started and then uncomment the line below. // import "./user_socket.js" @@ -26,6 +22,8 @@ import { Socket } from "phoenix"; import { LiveSocket } from "phoenix_live_view"; import topbar from "../vendor/topbar"; +import "../css/app.css"; + let csrfToken = document .querySelector("meta[name='csrf-token']") .getAttribute("content"); @@ -35,8 +33,8 @@ let liveSocket = new LiveSocket("/live", Socket, { // Show progress bar on live navigation and form submits topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" }); -window.addEventListener("phx:page-loading-start", (info) => topbar.show()); -window.addEventListener("phx:page-loading-stop", (info) => topbar.hide()); +window.addEventListener("phx:page-loading-start", (_info) => topbar.show(300)); +window.addEventListener("phx:page-loading-stop", (_info) => topbar.hide()); // connect if there are any LiveViews on the page liveSocket.connect(); diff --git a/demo/assets/js/storybook.js b/demo/assets/js/storybook.js index 863cce89..11daed55 100644 --- a/demo/assets/js/storybook.js +++ b/demo/assets/js/storybook.js @@ -9,3 +9,26 @@ // (function () { // window.storybook = { Hooks, Params, Uploaders }; // })(); + +// If your components require alpinejs, you'll need to start +// alpine after the DOM is loaded and pass in an onBeforeElUpdated +// +// import Alpine from 'alpinejs' +// window.Alpine = Alpine +// document.addEventListener('DOMContentLoaded', () => { +// window.Alpine.start(); +// }); + +// (function () { +// window.storybook = { +// LiveSocketOptions: { +// dom: { +// onBeforeElUpdated(from, to) { +// if (from._x_dataStack) { +// window.Alpine.clone(from, to) +// } +// } +// } +// } +// }; +// })(); diff --git a/demo/assets/vendor/topbar.js b/demo/assets/vendor/topbar.js index 1f622097..41957274 100644 --- a/demo/assets/vendor/topbar.js +++ b/demo/assets/vendor/topbar.js @@ -1,6 +1,6 @@ /** * @license MIT - * topbar 1.0.0, 2021-01-06 + * topbar 2.0.0, 2023-02-04 * https://buunguyen.github.io/topbar * Copyright (c) 2021 Buu Nguyen */ @@ -35,10 +35,11 @@ })(); var canvas, - progressTimerId, - fadeTimerId, currentProgress, showing, + progressTimerId = null, + fadeTimerId = null, + delayTimerId = null, addEvent = function (elem, type, handler) { if (elem.addEventListener) elem.addEventListener(type, handler, false); else if (elem.attachEvent) elem.attachEvent("on" + type, handler); @@ -95,21 +96,26 @@ for (var key in opts) if (options.hasOwnProperty(key)) options[key] = opts[key]; }, - show: function () { + show: function (delay) { if (showing) return; - showing = true; - if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId); - if (!canvas) createCanvas(); - canvas.style.opacity = 1; - canvas.style.display = "block"; - topbar.progress(0); - if (options.autoRun) { - (function loop() { - progressTimerId = window.requestAnimationFrame(loop); - topbar.progress( - "+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2) - ); - })(); + if (delay) { + if (delayTimerId) return; + delayTimerId = setTimeout(() => topbar.show(), delay); + } else { + showing = true; + if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId); + if (!canvas) createCanvas(); + canvas.style.opacity = 1; + canvas.style.display = "block"; + topbar.progress(0); + if (options.autoRun) { + (function loop() { + progressTimerId = window.requestAnimationFrame(loop); + topbar.progress( + "+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2) + ); + })(); + } } }, progress: function (to) { @@ -125,6 +131,8 @@ return currentProgress; }, hide: function () { + clearTimeout(delayTimerId); + delayTimerId = null; if (!showing) return; showing = false; if (progressTimerId != null) { diff --git a/demo/config/config.exs b/demo/config/config.exs index 80fca9c4..5c44026b 100644 --- a/demo/config/config.exs +++ b/demo/config/config.exs @@ -7,20 +7,23 @@ # General application configuration import Config +config :demo, + generators: [timestamp_type: :utc_datetime] + # Configures the endpoint config :demo, DemoWeb.Endpoint, url: [host: "localhost"], + adapter: Phoenix.Endpoint.Cowboy2Adapter, render_errors: [ - view: DemoWeb.ErrorView, - accepts: ~w(html json), + formats: [html: DemoWeb.ErrorHTML, json: DemoWeb.ErrorJSON], layout: false ], pubsub_server: Demo.PubSub, - live_view: [signing_salt: "njO9CwWg"] + live_view: [signing_salt: "hha5Qn8m"] # Configure esbuild (the version is required) config :esbuild, - version: "0.14.29", + version: "0.17.11", default: [ args: ~w(js/app.js js/storybook.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), diff --git a/demo/config/dev.exs b/demo/config/dev.exs index 3c698716..8dabcdd3 100644 --- a/demo/config/dev.exs +++ b/demo/config/dev.exs @@ -4,8 +4,8 @@ import Config # debugging and code reloading. # # The watchers configuration can be used to run external -# watchers to your application. For example, we use it -# with esbuild to bundle .js and .css sources. +# watchers to your application. For example, we can use it +# to bundle .js and .css sources. config :demo, DemoWeb.Endpoint, # Binding to loopback ipv4 address prevents access from other machines. # Change to `ip: {0, 0, 0, 0}` to allow access from other machines. @@ -13,12 +13,9 @@ config :demo, DemoWeb.Endpoint, check_origin: false, code_reloader: true, debug_errors: true, - secret_key_base: - "Jc+vuCcFoG0H5DxSRJrLBTm/ulv+34QXD1EfBNdRu9CF+hZvoioZ0FPB2bLrkDtc", + secret_key_base: "7I09KWxcUUDVCHxt1eWTxz/zRZ4K9839EiL/iutqCNQORX+I2K62EM4/Y8HH+60G", watchers: [ - # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args) - esbuild: - {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]} + esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]} ] # ## SSL Support @@ -29,7 +26,6 @@ config :demo, DemoWeb.Endpoint, # # mix phx.gen.cert # -# Note that this task requires Erlang/OTP 20 or later. # Run `mix help phx.gen.cert` for more information. # # The `http:` config above can be replaced with: @@ -51,12 +47,14 @@ config :demo, DemoWeb.Endpoint, patterns: [ ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", ~r"priv/gettext/.*(po)$", - ~r"lib/demo_web/(live|views)/.*(ex)$", - ~r"lib/demo_web/templates/.*(eex)$", + ~r"lib/demo_web/(controllers|live|components)/.*(ex|heex)$", ~r"storybook/.*(exs)$" ] ] +# Enable dev routes for dashboard and mailbox +config :demo, dev_routes: true + # Do not include metadata nor timestamps in development logs config :logger, :console, format: "[$level] $message\n" @@ -66,3 +64,6 @@ config :phoenix, :stacktrace_depth, 20 # Initialize plugs at runtime for faster development compilation config :phoenix, :plug_init_mode, :runtime + +# Include HEEx debug annotations as HTML comments in rendered markup +config :phoenix_live_view, :debug_heex_annotations, true diff --git a/demo/config/prod.exs b/demo/config/prod.exs index 68e31b45..ff10aa9b 100644 --- a/demo/config/prod.exs +++ b/demo/config/prod.exs @@ -1,50 +1,14 @@ import Config -# For production, don't forget to configure the url host -# to something meaningful, Phoenix uses this information -# when generating URLs. -# # Note we also include the path to a cache manifest # containing the digested version of static files. This -# manifest is generated by the `mix phx.digest` task, +# manifest is generated by the `mix assets.deploy` task, # which you should run after static files are built and # before starting your production server. -config :demo, DemoWeb.Endpoint, - cache_static_manifest: "priv/static/cache_manifest.json" +config :demo, DemoWeb.Endpoint, cache_static_manifest: "priv/static/cache_manifest.json" # Do not print debug messages in production config :logger, level: :info -# ## SSL Support -# -# To get SSL working, you will need to add the `https` key -# to the previous section and set your `:url` port to 443: -# -# config :demo, DemoWeb.Endpoint, -# ..., -# url: [host: "example.com", port: 443], -# https: [ -# ..., -# port: 443, -# cipher_suite: :strong, -# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), -# certfile: System.get_env("SOME_APP_SSL_CERT_PATH") -# ] -# -# The `cipher_suite` is set to `:strong` to support only the -# latest and more secure SSL ciphers. This means old browsers -# and clients may not be supported. You can set it to -# `:compatible` for wider support. -# -# `:keyfile` and `:certfile` expect an absolute path to the key -# and cert in disk or a relative path inside priv, for example -# "priv/ssl/server.key". For all supported SSL configuration -# options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1 -# -# We also recommend setting `force_ssl` in your endpoint, ensuring -# no data is ever sent via http, always redirecting to https: -# -# config :demo, DemoWeb.Endpoint, -# force_ssl: [hsts: true] -# -# Check `Plug.SSL` for all available options in `force_ssl`. +# Runtime production configuration, including reading +# of environment variables, is done on config/runtime.exs. diff --git a/demo/config/runtime.exs b/demo/config/runtime.exs index 55349fe2..1c8501ee 100644 --- a/demo/config/runtime.exs +++ b/demo/config/runtime.exs @@ -36,6 +36,8 @@ if config_env() == :prod do host = System.get_env("PHX_HOST") || "example.com" port = String.to_integer(System.get_env("PORT") || "4000") + config :demo, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY") + config :demo, DemoWeb.Endpoint, url: [host: host, port: 443, scheme: "https"], http: [ @@ -47,4 +49,36 @@ if config_env() == :prod do port: port ], secret_key_base: secret_key_base + + # ## SSL Support + # + # To get SSL working, you will need to add the `https` key + # to your endpoint configuration: + # + # config :demo, DemoWeb.Endpoint, + # https: [ + # ..., + # port: 443, + # cipher_suite: :strong, + # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), + # certfile: System.get_env("SOME_APP_SSL_CERT_PATH") + # ] + # + # The `cipher_suite` is set to `:strong` to support only the + # latest and more secure SSL ciphers. This means old browsers + # and clients may not be supported. You can set it to + # `:compatible` for wider support. + # + # `:keyfile` and `:certfile` expect an absolute path to the key + # and cert in disk or a relative path inside priv, for example + # "priv/ssl/server.key". For all supported SSL configuration + # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1 + # + # We also recommend setting `force_ssl` in your endpoint, ensuring + # no data is ever sent via http, always redirecting to https: + # + # config :demo, DemoWeb.Endpoint, + # force_ssl: [hsts: true] + # + # Check `Plug.SSL` for all available options in `force_ssl`. end diff --git a/demo/config/test.exs b/demo/config/test.exs index df7402d5..90e81ac8 100644 --- a/demo/config/test.exs +++ b/demo/config/test.exs @@ -4,12 +4,11 @@ import Config # you can enable the server option below. config :demo, DemoWeb.Endpoint, http: [ip: {127, 0, 0, 1}, port: 4002], - secret_key_base: - "bZKkqegn+ndkcxTO9Hrb/Go/rbZy9YP6dku/V1PeEiLM9dZdI4qPoy3NrftWxk2t", + secret_key_base: "UwBHokdfX1j1Z/VYF+Cj4RHuS8iaMV2xTzmET+mbzBUF7dVI9wvgo+quplDOVGws", server: false # Print only warnings and errors during test -config :logger, level: :warn +config :logger, level: :warning # Initialize plugs at runtime for faster test compilation config :phoenix, :plug_init_mode, :runtime diff --git a/demo/lib/demo/application.ex b/demo/lib/demo/application.ex index 3033f5ca..6412e9ae 100644 --- a/demo/lib/demo/application.ex +++ b/demo/lib/demo/application.ex @@ -8,14 +8,13 @@ defmodule Demo.Application do @impl true def start(_type, _args) do children = [ - # Start the Telemetry supervisor DemoWeb.Telemetry, - # Start the PubSub system + {DNSCluster, query: Application.get_env(:demo, :dns_cluster_query) || :ignore}, {Phoenix.PubSub, name: Demo.PubSub}, - # Start the Endpoint (http/https) - DemoWeb.Endpoint # Start a worker by calling: Demo.Worker.start_link(arg) - # {Demo.Worker, arg} + # {Demo.Worker, arg}, + # Start to serve requests, typically the last entry + DemoWeb.Endpoint ] # See https://hexdocs.pm/elixir/Supervisor.html diff --git a/demo/lib/demo_web.ex b/demo/lib/demo_web.ex index dfe3db94..3f0a1d53 100644 --- a/demo/lib/demo_web.ex +++ b/demo/lib/demo_web.ex @@ -1,53 +1,60 @@ defmodule DemoWeb do @moduledoc """ The entrypoint for defining your web interface, such - as controllers, views, channels and so on. + as controllers, components, channels, and so on. This can be used in your application as: use DemoWeb, :controller - use DemoWeb, :view + use DemoWeb, :html - The definitions below will be executed for every view, - controller, etc, so keep them short and clean, focused + The definitions below will be executed for every controller, + component, etc, so keep them short and clean, focused on imports, uses and aliases. Do NOT define functions inside the quoted expressions - below. Instead, define any helper function in modules - and import those modules here. + below. Instead, define additional modules and import + those modules here. """ - def controller do + def static_paths, do: ~w(assets fonts images favicon.ico robots.txt) + + def router do quote do - use Phoenix.Controller, namespace: DemoWeb + use Phoenix.Router, helpers: false + # Import common connection and controller functions to use in pipelines import Plug.Conn - import DemoWeb.Gettext - alias DemoWeb.Router.Helpers, as: Routes + import Phoenix.Controller + import Phoenix.LiveView.Router + end + end + + def channel do + quote do + use Phoenix.Channel end end - def view do + def controller do quote do - use Phoenix.View, - root: "lib/demo_web/templates", - namespace: DemoWeb + use Phoenix.Controller, + formats: [:html, :json], + layouts: [html: DemoWeb.Layouts] - # Import convenience functions from controllers - import Phoenix.Controller, - only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1] + import Plug.Conn + import DemoWeb.Gettext - # Include shared imports and aliases for views - unquote(view_helpers()) + unquote(verified_routes()) end end def live_view do quote do use Phoenix.LiveView, - layout: {DemoWeb.LayoutView, "live.html"} + layout: {DemoWeb.Layouts, :app} - unquote(view_helpers()) + unquote(html_helpers()) end end @@ -55,48 +62,44 @@ defmodule DemoWeb do quote do use Phoenix.LiveComponent - unquote(view_helpers()) + unquote(html_helpers()) end end - def component do + def html do quote do use Phoenix.Component - unquote(view_helpers()) - end - end - - def router do - quote do - use Phoenix.Router + # Import convenience functions from controllers + import Phoenix.Controller, + only: [get_csrf_token: 0, view_module: 1, view_template: 1] - import Plug.Conn - import Phoenix.Controller - import Phoenix.LiveView.Router + # Include general helpers for rendering HTML + unquote(html_helpers()) end end - def channel do + defp html_helpers do quote do - use Phoenix.Channel + # HTML escaping functionality + import Phoenix.HTML + # translation import DemoWeb.Gettext + + # Shortcut for generating JS commands + alias Phoenix.LiveView.JS + + # Routes generation with the ~p sigil + unquote(verified_routes()) end end - defp view_helpers do + def verified_routes do quote do - # Use all HTML functionality (forms, tags, etc) - use Phoenix.HTML - - # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc) - import Phoenix.LiveView.Helpers - - # Import basic rendering functionality (render, render_layout, etc) - import Phoenix.View - - import DemoWeb.Gettext - alias DemoWeb.Router.Helpers, as: Routes + use Phoenix.VerifiedRoutes, + endpoint: DemoWeb.Endpoint, + router: DemoWeb.Router, + statics: DemoWeb.static_paths() end end diff --git a/demo/lib/demo_web/components/layouts.ex b/demo/lib/demo_web/components/layouts.ex new file mode 100644 index 00000000..58a4271f --- /dev/null +++ b/demo/lib/demo_web/components/layouts.ex @@ -0,0 +1,5 @@ +defmodule DemoWeb.Layouts do + use DemoWeb, :html + + embed_templates "layouts/*" +end diff --git a/demo/lib/demo_web/components/layouts/app.html.heex b/demo/lib/demo_web/components/layouts/app.html.heex new file mode 100644 index 00000000..b6d6344b --- /dev/null +++ b/demo/lib/demo_web/components/layouts/app.html.heex @@ -0,0 +1,32 @@ +
+
+
+ + + +

+ v<%= Application.spec(:phoenix, :vsn) %> +

+
+ +
+
+
+
+ + <%= @inner_content %> +
+
diff --git a/demo/lib/demo_web/components/layouts/root.html.heex b/demo/lib/demo_web/components/layouts/root.html.heex new file mode 100644 index 00000000..d3b6ca68 --- /dev/null +++ b/demo/lib/demo_web/components/layouts/root.html.heex @@ -0,0 +1,17 @@ + + + + + + + <.live_title suffix=" ยท Phoenix Framework"> + <%= assigns[:page_title] || "Demo" %> + + + + + + <%= @inner_content %> + + diff --git a/demo/lib/demo_web/controllers/error_html.ex b/demo/lib/demo_web/controllers/error_html.ex new file mode 100644 index 00000000..d59f0475 --- /dev/null +++ b/demo/lib/demo_web/controllers/error_html.ex @@ -0,0 +1,19 @@ +defmodule DemoWeb.ErrorHTML do + use DemoWeb, :html + + # If you want to customize your error pages, + # uncomment the embed_templates/1 call below + # and add pages to the error directory: + # + # * lib/demo_web/controllers/error_html/404.html.heex + # * lib/demo_web/controllers/error_html/500.html.heex + # + # embed_templates "error_html/*" + + # The default is to render a plain text page based on + # the template name. For example, "404.html" becomes + # "Not Found". + def render(template, _assigns) do + Phoenix.Controller.status_message_from_template(template) + end +end diff --git a/demo/lib/demo_web/controllers/error_json.ex b/demo/lib/demo_web/controllers/error_json.ex new file mode 100644 index 00000000..53154fa5 --- /dev/null +++ b/demo/lib/demo_web/controllers/error_json.ex @@ -0,0 +1,15 @@ +defmodule DemoWeb.ErrorJSON do + # If you want to customize a particular status code, + # you may add your own clauses, such as: + # + # def render("500.json", _assigns) do + # %{errors: %{detail: "Internal Server Error"}} + # end + + # By default, Phoenix returns the status message from + # the template name. For example, "404.json" becomes + # "Not Found". + def render(template, _assigns) do + %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}} + end +end diff --git a/demo/lib/demo_web/endpoint.ex b/demo/lib/demo_web/endpoint.ex index ee956db1..b0271626 100644 --- a/demo/lib/demo_web/endpoint.ex +++ b/demo/lib/demo_web/endpoint.ex @@ -7,11 +7,11 @@ defmodule DemoWeb.Endpoint do @session_options [ store: :cookie, key: "_demo_key", - signing_salt: "lzVFwZoF" + signing_salt: "i7E381Kh", + same_site: "Lax" ] - socket "/live", Phoenix.LiveView.Socket, - websocket: [connect_info: [session: @session_options]] + socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] # Serve at "/" the static files from "priv/static" directory. # @@ -21,7 +21,7 @@ defmodule DemoWeb.Endpoint do at: "/", from: :demo, gzip: false, - only: ~w(assets fonts images favicon.ico robots.txt) + only: DemoWeb.static_paths() # Code reloading can be explicitly enabled under the # :code_reloader configuration of your endpoint. diff --git a/demo/lib/demo_web/router.ex b/demo/lib/demo_web/router.ex index 94ebc57d..92719730 100644 --- a/demo/lib/demo_web/router.ex +++ b/demo/lib/demo_web/router.ex @@ -1,12 +1,13 @@ defmodule DemoWeb.Router do use DemoWeb, :router - import PhxLiveStorybook.Router + import PhoenixStorybook.Router pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash + plug :put_root_layout, html: {DemoWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers end diff --git a/demo/lib/demo_web/storybook.ex b/demo/lib/demo_web/storybook.ex index c4bd0821..f4b6d7e3 100644 --- a/demo/lib/demo_web/storybook.ex +++ b/demo/lib/demo_web/storybook.ex @@ -1,7 +1,7 @@ defmodule DemoWeb.Storybook do - use PhxLiveStorybook, + use PhoenixStorybook, otp_app: :demo_web, - content_path: Path.expand("../../../priv/storybook", __DIR__), + content_path: Path.expand("../../storybook", __DIR__), # assets path are remote path, not local file-system paths css_path: "/assets/storybook.css", js_path: "/assets/storybook.js", diff --git a/demo/lib/demo_web/telemetry.ex b/demo/lib/demo_web/telemetry.ex index d5cd56a4..77050116 100644 --- a/demo/lib/demo_web/telemetry.ex +++ b/demo/lib/demo_web/telemetry.ex @@ -22,13 +22,34 @@ defmodule DemoWeb.Telemetry do def metrics do [ # Phoenix Metrics + summary("phoenix.endpoint.start.system_time", + unit: {:native, :millisecond} + ), summary("phoenix.endpoint.stop.duration", unit: {:native, :millisecond} ), + summary("phoenix.router_dispatch.start.system_time", + tags: [:route], + unit: {:native, :millisecond} + ), + summary("phoenix.router_dispatch.exception.duration", + tags: [:route], + unit: {:native, :millisecond} + ), summary("phoenix.router_dispatch.stop.duration", tags: [:route], unit: {:native, :millisecond} ), + summary("phoenix.socket_connected.duration", + unit: {:native, :millisecond} + ), + summary("phoenix.channel_joined.duration", + unit: {:native, :millisecond} + ), + summary("phoenix.channel_handled_in.duration", + tags: [:event], + unit: {:native, :millisecond} + ), # VM Metrics summary("vm.memory.total", unit: {:byte, :kilobyte}), diff --git a/demo/lib/demo_web/views/error_view.ex b/demo/lib/demo_web/views/error_view.ex deleted file mode 100644 index c0ccfcb9..00000000 --- a/demo/lib/demo_web/views/error_view.ex +++ /dev/null @@ -1,16 +0,0 @@ -defmodule DemoWeb.ErrorView do - use DemoWeb, :view - - # If you want to customize a particular status code - # for a certain format, you may uncomment below. - # def render("500.html", _assigns) do - # "Internal Server Error" - # end - - # By default, Phoenix returns the status message from - # the template name. For example, "404.html" becomes - # "Not Found". - def template_not_found(template, _assigns) do - Phoenix.Controller.status_message_from_template(template) - end -end diff --git a/demo/mix.exs b/demo/mix.exs index d201ef35..fc6ceecf 100644 --- a/demo/mix.exs +++ b/demo/mix.exs @@ -5,9 +5,8 @@ defmodule Demo.MixProject do [ app: :demo, version: "0.1.0", - elixir: "~> 1.12", + elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), - compilers: Mix.compilers(), start_permanent: Mix.env() == :prod, aliases: aliases(), deps: deps() @@ -33,18 +32,20 @@ defmodule Demo.MixProject do # Type `mix help deps` for examples and options. defp deps do [ - {:phoenix, "~> 1.6.14"}, - {:phoenix_html, "~> 3.0"}, + {:phoenix, "~> 1.7.10"}, + {:phoenix_html, "~> 3.3"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, - {:phoenix_live_view, "~> 0.18.0"}, + {:phoenix_live_view, "~> 0.20.1"}, {:floki, ">= 0.30.0", only: :test}, - {:esbuild, "~> 0.4", runtime: Mix.env() == :dev}, + {:esbuild, "~> 0.8", runtime: Mix.env() == :dev}, {:telemetry_metrics, "~> 0.6"}, {:telemetry_poller, "~> 1.0"}, - {:gettext, "~> 0.18"}, + {:gettext, "~> 0.20"}, {:jason, "~> 1.2"}, + {:dns_cluster, "~> 0.1.1"}, {:plug_cowboy, "~> 2.5"}, - {:phx_live_storybook, "~> 0.4.5"}, + {:phoenix_storybook, "~> 0.5.0"}, + {:heroicons, "~> 0.5.3"}, {:doggo, path: ".."} ] end @@ -57,7 +58,9 @@ defmodule Demo.MixProject do # See the documentation for `Mix` for more info on aliases. defp aliases do [ - setup: ["deps.get"], + setup: ["deps.get", "assets.setup", "assets.build"], + "assets.setup": ["esbuild.install --if-missing"], + "assets.build": ["esbuild default"], "assets.deploy": ["esbuild default --minify", "phx.digest"] ] end diff --git a/demo/mix.lock b/demo/mix.lock index b9d94924..aa74a556 100644 --- a/demo/mix.lock +++ b/demo/mix.lock @@ -1,34 +1,38 @@ %{ - "castore": {:hex, :castore, "0.1.18", "deb5b9ab02400561b6f5708f3e7660fc35ca2d51bfc6a940d2f513f89c2975fc", [:mix], [], "hexpm", "61bbaf6452b782ef80b33cdb45701afbcf0a918a45ebe7e73f1130d661e66a06"}, - "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, + "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, - "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, - "earmark": {:hex, :earmark, "1.4.30", "e440ac556aad2e06dab679d2f0a2e25ed8b617d5f9a051109ff97aed780895df", [:mix], [{:earmark_parser, "~> 1.4.27", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "4596a99dae36324078bfef1e08e54418309b8836e29d812c117a484f7a5a09d8"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.28", "0bf6546eb7cd6185ae086cbc5d20cd6dbb4b428aad14c02c49f7b554484b4586", [:mix], [], "hexpm", "501cef12286a3231dc80c81352a9453decf9586977f917a96e619293132743fb"}, - "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"}, + "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, + "dns_cluster": {:hex, :dns_cluster, "0.1.1", "73b4b2c3ec692f8a64276c43f8c929733a9ab9ac48c34e4c0b3d9d1b5cd69155", [:mix], [], "hexpm", "03a3f6ff16dcbb53e219b99c7af6aab29eb6b88acf80164b4bd76ac18dc890b3"}, + "earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"}, + "esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"}, + "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"}, - "gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"}, - "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, + "floki": {:hex, :floki, "0.35.2", "87f8c75ed8654b9635b311774308b2760b47e9a579dabf2e4d5f1e1d42c39e0b", [:mix], [], "hexpm", "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"}, + "gettext": {:hex, :gettext, "0.23.1", "821e619a240e6000db2fc16a574ef68b3bd7fe0167ccc264a81563cc93e67a31", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "19d744a36b809d810d610b57c27b934425859d158ebd56561bc41f7eeb8795db"}, + "heroicons": {:hex, :heroicons, "0.5.3", "ee8ae8335303df3b18f2cc07f46e1cb6e761ba4cf2c901623fbe9a28c0bc51dd", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:phoenix_live_view, ">= 0.18.2", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "a210037e8a09ac17e2a0a0779d729e89c821c944434c3baa7edfc1f5b32f3502"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_eex": {:hex, :makeup_eex, "0.1.1", "89352d5da318d97ae27bbcc87201f274504d2b71ede58ca366af6a5fbed9508d", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.16", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_html, "~> 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d111a0994eaaab09ef1a4b3b313ef806513bb4652152c26c0d7ca2be8402a964"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, - "makeup_html": {:hex, :makeup_html, "0.1.0", "b0228fda985e311d8f0d25bed58f8280826633a38d7448cabdd723e116165bcf", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "0ca44e7dcb8d933e010740324470dd8ec947243b51304bd34b8165ef3281edc2"}, - "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, - "phoenix": {:hex, :phoenix, "1.6.14", "57678366dc1d5bad49832a0fc7f12c2830c10d3eacfad681bfe9602cd4445f04", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d48c0da00b3d4cd1aad6055387917491af9f6e1f1e96cedf6c6b7998df9dba26"}, - "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, - "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.2", "635cf07de947235deb030cd6b776c71a3b790ab04cebf526aa8c879fe17c7784", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6 or ~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da287a77327e996cc166e4c440c3ad5ab33ccdb151b91c793209b39ebbce5b75"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, - "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, - "phx_live_storybook": {:hex, :phx_live_storybook, "0.4.5", "a14d33a81caa751e1af8e3e72c638595cf6cccbcabba20cc6821f3c028f1d5d1", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:heroicons, "~> 0.5", [hex: :heroicons, repo: "hexpm", optional: true]}, {:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: true]}, {:makeup_eex, "~> 0.1.0", [hex: :makeup_eex, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "84faeb19a516e474b5732ea3c905f7a920ec3371fd064da306abd62778f5db12"}, - "plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, + "makeup_html": {:hex, :makeup_html, "0.1.1", "c3d4abd39d5f7e925faca72ada6e9cc5c6f5fa7cd5bc0158315832656cf14d7f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "44f2a61bc5243645dd7fafeaa6cc28793cd22f3c76b861e066168f9a5b2c26a4"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"}, + "phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"}, + "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "0.20.1", "92a37acf07afca67ac98bd326532ba8f44ad7d4bdf3e4361b03f7f02594e5ae9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be494fd1215052729298b0e97d5c2ce8e719c00854b82cd8cf15c1cd7fcf6294"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, + "phoenix_storybook": {:hex, :phoenix_storybook, "0.5.7", "7424365f25ce864971acc16e0a8669e286e41454c02707f7d567205411638f88", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:heroicons, "~> 0.5", [hex: :heroicons, repo: "hexpm", optional: true]}, {:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: true]}, {:makeup_eex, "~> 0.1.0", [hex: :makeup_eex, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_live_view, "> 0.18.7", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "c9a885987346fd1d57d1058021c2b830c117fc8bd871e6d8e86cfb5d074337ce"}, + "phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"}, + "phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"}, + "plug": {:hex, :plug, "1.15.2", "94cf1fa375526f30ff8770837cb804798e0045fd97185f0bb9e5fcd858c792a3", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02731fa0c2dcb03d8d21a1d941bdbbe99c2946c0db098eee31008e04c6283615"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, + "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, - "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, + "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"}, } diff --git a/demo/priv/static/favicon.ico b/demo/priv/static/favicon.ico index 73de524a..7f372bfc 100644 Binary files a/demo/priv/static/favicon.ico and b/demo/priv/static/favicon.ico differ diff --git a/demo/priv/static/images/phoenix.png b/demo/priv/static/images/phoenix.png deleted file mode 100644 index 9c81075f..00000000 Binary files a/demo/priv/static/images/phoenix.png and /dev/null differ diff --git a/demo/storybook/_root.index.exs b/demo/storybook/_root.index.exs new file mode 100644 index 00000000..5a15c175 --- /dev/null +++ b/demo/storybook/_root.index.exs @@ -0,0 +1,16 @@ +defmodule Storybook.Root do + # See https://hexdocs.pm/phoenix_storybook/PhoenixStorybook.Index.html for full index + # documentation. + + use PhoenixStorybook.Index + + def folder_icon, do: {:fa, "book-open", :light, "lsb-mr-1"} + def folder_name, do: "Doggo" + + def entry("welcome") do + [ + name: "Welcome Page", + icon: {:fa, "hand-wave", :thin} + ] + end +end diff --git a/demo/storybook/components/app_bar.story.exs b/demo/storybook/components/app_bar.story.exs new file mode 100644 index 00000000..6d61965b --- /dev/null +++ b/demo/storybook/components/app_bar.story.exs @@ -0,0 +1,29 @@ +defmodule Storybook.CoreComponents.List do + use PhoenixStorybook.Story, :component + + def function, do: &Doggo.app_bar/1 + + def variations do + [ + %Variation{ + id: :default, + attributes: %{ + title: "Page title" + }, + slots: [ + """ + <:navigation label="Toggle menu" on_click={%Phoenix.LiveView.JS{}}> + + + <:action label="Search" on_click={%Phoenix.LiveView.JS{}}> + + + <:action label="Like" on_click={%Phoenix.LiveView.JS{}}> + + + """ + ] + } + ] + end +end diff --git a/demo/storybook/components/property_list.story.exs b/demo/storybook/components/property_list.story.exs new file mode 100644 index 00000000..e8014e4f --- /dev/null +++ b/demo/storybook/components/property_list.story.exs @@ -0,0 +1,17 @@ +defmodule Storybook.CoreComponents.List do + use PhoenixStorybook.Story, :component + + def function, do: &Doggo.property_list/1 + + def variations do + [ + %Variation{ + id: :default, + slots: [ + ~s|<:prop label="Name">George|, + ~s|<:prop label="Age">42| + ] + } + ] + end +end diff --git a/demo/storybook/welcome.story.exs b/demo/storybook/welcome.story.exs new file mode 100644 index 00000000..6bc9a387 --- /dev/null +++ b/demo/storybook/welcome.story.exs @@ -0,0 +1,100 @@ +defmodule Storybook.MyPage do + # See https://hexdocs.pm/phoenix_storybook/PhoenixStorybook.Story.html for full story + # documentation. + use PhoenixStorybook.Story, :page + + def doc, do: "Your very first steps into using Phoenix Storybook" + + # Declare an optional tab-based navigation in your page: + def navigation do + [ + {:welcome, "Welcome", {:fa, "hand-wave", :thin}}, + {:components, "Components", {:fa, "toolbox", :thin}}, + {:sandboxing, "Sandboxing", {:fa, "box-check", :thin}}, + {:icons, "Icons", {:fa, "icons", :thin}} + ] + end + + # This is a dummy fonction that you should replace with your own HEEx content. + def render(assigns = %{tab: :welcome}) do + ~H""" +
+

+ We generated your storybook with an example of a page and a component. + Explore the generated *.story.exs + files in your /storybook + directory. When you're ready to add your own, just drop your new story & index files into the same directory and refresh your storybook. +

+ +

+ Here are a few docs you might be interested in: +

+ + <.description_list items={[ + {"Create a new Story", doc_link("Story")}, + {"Display components using Variations", doc_link("Stories.Variation")}, + {"Group components using VariationGroups", doc_link("Stories.VariationGroup")}, + {"Organize the sidebar with Index files", doc_link("Index")} + ]} /> + +

+ This should be enough to get you started, but you can use the tabs in the upper-right corner of this page to check out advanced usage guides. +

+
+ """ + end + + def render(assigns = %{tab: guide}) when guide in ~w(components sandboxing icons)a do + assigns = + assign(assigns, + guide: guide, + guide_content: PhoenixStorybook.Guides.markup("#{guide}.md") + ) + + ~H""" +

+ + This and other guides are also available on HexDocs. + +

+
+ <%= Phoenix.HTML.raw(@guide_content) %> +
+ """ + end + + defp description_list(assigns) do + ~H""" +
+
+
+ <%= for {dt, link} <- @items do %> +
+
+ <%= dt %> +
+
+ + <%= link %> + +
+
+ <% end %> +
+
+
+ """ + end + + defp doc_link(page) do + "https://hexdocs.pm/phoenix_storybook/PhoenixStorybook.#{page}.html" + end +end diff --git a/demo/test/demo_web/controllers/error_html_test.exs b/demo/test/demo_web/controllers/error_html_test.exs new file mode 100644 index 00000000..aaf8e9fd --- /dev/null +++ b/demo/test/demo_web/controllers/error_html_test.exs @@ -0,0 +1,14 @@ +defmodule DemoWeb.ErrorHTMLTest do + use DemoWeb.ConnCase, async: true + + # Bring render_to_string/4 for testing custom views + import Phoenix.Template + + test "renders 404.html" do + assert render_to_string(DemoWeb.ErrorHTML, "404", "html", []) == "Not Found" + end + + test "renders 500.html" do + assert render_to_string(DemoWeb.ErrorHTML, "500", "html", []) == "Internal Server Error" + end +end diff --git a/demo/test/demo_web/controllers/error_json_test.exs b/demo/test/demo_web/controllers/error_json_test.exs new file mode 100644 index 00000000..aa127306 --- /dev/null +++ b/demo/test/demo_web/controllers/error_json_test.exs @@ -0,0 +1,12 @@ +defmodule DemoWeb.ErrorJSONTest do + use DemoWeb.ConnCase, async: true + + test "renders 404" do + assert DemoWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}} + end + + test "renders 500" do + assert DemoWeb.ErrorJSON.render("500.json", %{}) == + %{errors: %{detail: "Internal Server Error"}} + end +end diff --git a/demo/test/demo_web/controllers/page_controller_test.exs b/demo/test/demo_web/controllers/page_controller_test.exs deleted file mode 100644 index f4771980..00000000 --- a/demo/test/demo_web/controllers/page_controller_test.exs +++ /dev/null @@ -1,8 +0,0 @@ -defmodule DemoWeb.PageControllerTest do - use DemoWeb.ConnCase - - test "GET /", %{conn: conn} do - conn = get(conn, "/") - assert html_response(conn, 200) =~ "Welcome to Phoenix!" - end -end diff --git a/demo/test/demo_web/views/error_view_test.exs b/demo/test/demo_web/views/error_view_test.exs deleted file mode 100644 index 9d5a88e4..00000000 --- a/demo/test/demo_web/views/error_view_test.exs +++ /dev/null @@ -1,15 +0,0 @@ -defmodule DemoWeb.ErrorViewTest do - use DemoWeb.ConnCase, async: true - - # Bring render/3 and render_to_string/3 for testing custom views - import Phoenix.View - - test "renders 404.html" do - assert render_to_string(DemoWeb.ErrorView, "404.html", []) == "Not Found" - end - - test "renders 500.html" do - assert render_to_string(DemoWeb.ErrorView, "500.html", []) == - "Internal Server Error" - end -end diff --git a/demo/test/demo_web/views/layout_view_test.exs b/demo/test/demo_web/views/layout_view_test.exs deleted file mode 100644 index f5a8b730..00000000 --- a/demo/test/demo_web/views/layout_view_test.exs +++ /dev/null @@ -1,8 +0,0 @@ -defmodule DemoWeb.LayoutViewTest do - use DemoWeb.ConnCase, async: true - - # When testing helpers, you may want to import Phoenix.HTML and - # use functions such as safe_to_string() to convert the helper - # result into an HTML string. - # import Phoenix.HTML -end diff --git a/demo/test/demo_web/views/page_view_test.exs b/demo/test/demo_web/views/page_view_test.exs deleted file mode 100644 index 6ee178f8..00000000 --- a/demo/test/demo_web/views/page_view_test.exs +++ /dev/null @@ -1,3 +0,0 @@ -defmodule DemoWeb.PageViewTest do - use DemoWeb.ConnCase, async: true -end diff --git a/demo/test/support/conn_case.ex b/demo/test/support/conn_case.ex index 2e4b6dc3..e51004aa 100644 --- a/demo/test/support/conn_case.ex +++ b/demo/test/support/conn_case.ex @@ -19,15 +19,15 @@ defmodule DemoWeb.ConnCase do using do quote do + # The default endpoint for testing + @endpoint DemoWeb.Endpoint + + use DemoWeb, :verified_routes + # Import conveniences for testing with connections import Plug.Conn import Phoenix.ConnTest import DemoWeb.ConnCase - - alias DemoWeb.Router.Helpers, as: Routes - - # The default endpoint for testing - @endpoint DemoWeb.Endpoint end end