Some cleanups (#25)

* Remove old js files

* Cleanup styles

* Update page title

* Unify elements rounding

* Fix indentation

* Rename component modules

* Add actions to routes for better helpers
This commit is contained in:
Jonatan Kłosko 2021-02-11 16:35:32 +01:00 committed by GitHub
parent b487fe6104
commit 9d3a2ae264
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 338 additions and 1141 deletions

View file

@ -1,204 +1,12 @@
@tailwind base; @import "tailwindcss/base";
@tailwind components; @import "tailwindcss/components";
@tailwind utilities; @import "tailwindcss/utilities";
@import "../node_modules/nprogress/nprogress.css"; @import "nprogress/nprogress.css";
@import "./global.css";
@import "./components.css";
@import "./utilities.css";
@import "./live_view.css"; @import "./live_view.css";
@import "./markdown.css";
/* Remove the default outline on focused elements */ @import "./elixir_inspect.css";
:focus,
button:focus {
outline: none;
}
/* Hide Phoenix live reload frame */
iframe[hidden] {
display: none;
}
/* Markdown rendered content */
.markdown {
@apply text-gray-700;
}
.markdown h1 {
@apply text-gray-900 font-semibold text-3xl my-4;
}
.markdown h2 {
@apply text-gray-900 font-semibold text-2xl my-4;
}
.markdown h3 {
@apply text-gray-900 font-semibold text-xl my-4;
}
.markdown p {
@apply my-4;
}
.markdown ul {
@apply list-disc list-inside my-4;
}
.markdown ol {
@apply list-decimal list-inside my-4;
}
.markdown ul > li,
.markdown ol > li {
@apply my-1;
}
.markdown ul > li ul,
.markdown ol > li ol {
@apply ml-6;
}
.markdown blockquote {
@apply border-l-4 border-gray-200 pl-4 py-2 my-4 text-gray-500;
}
.markdown a {
@apply font-medium underline text-gray-900 hover:no-underline;
}
.markdown table {
@apply w-full my-4;
}
.markdown table thead tr {
@apply border-b border-gray-200;
}
.markdown table tbody tr:not(:last-child) {
@apply border-b border-gray-200;
}
.markdown table th {
@apply p-2 font-bold;
}
.markdown table td {
@apply p-2;
}
.markdown table th:first-child,
.markdown table td:first-child {
@apply pl-0;
}
.markdown table th:last-child,
.markdown table td:last-child {
@apply pr-0;
}
.markdown code {
@apply py-1 px-2 rounded text-sm align-middle;
/* Match the editor colors */
background-color: #282c34;
color: #abb2bf;
}
.markdown pre > code {
@apply block p-4 rounded text-sm align-middle;
/* Match the editor colors */
background-color: #282c34;
color: #abb2bf;
}
.markdown :first-child {
@apply mt-0;
}
.markdown :last-child {
@apply mb-0;
}
/* Elixir HTML-ized inspect result */
.elixir-inspect .atom {
color: #61afef;
}
.elixir-inspect .binary {
color: #5c6370;
}
.elixir-inspect .boolean {
color: #c678dd;
}
.elixir-inspect .list {
color: #5c6370;
}
.elixir-inspect .map {
color: #5c6370;
}
.elixir-inspect .nil {
color: #c678dd;
}
.elixir-inspect .number {
color: #d19a66;
}
.elixir-inspect .regex {
color: #e06c75;
}
.elixir-inspect .string {
color: #98c379;
}
.elixir-inspect .tuple {
color: #5c6370;
}
/* Other */
.bg-editor {
background-color: #282c34;
}
.font-editor {
font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback";
font-size: 14px;
}
.tiny-scrollbar::-webkit-scrollbar {
width: 0.4rem;
height: 0.4rem;
}
.tiny-scrollbar::-webkit-scrollbar-thumb {
border-radius: 0.25rem;
@apply bg-gray-400;
}
.tiny-scrollbar::-webkit-scrollbar-track {
@apply bg-gray-100;
}
.button-base {
@apply px-4 py-2 bg-white rounded-md border border-gray-300 font-medium text-gray-700 hover:bg-gray-50;
}
.button-sm {
@apply px-3 py-1;
}
.button-danger {
@apply border border-red-300 text-red-500 hover:bg-red-50;
}
.button-primary {
@apply border-0 bg-purple-400 text-white hover:bg-purple-500;
}
.input-base {
@apply w-full px-3 py-3 bg-white rounded-md placeholder-gray-400 text-gray-700;
}

39
assets/css/components.css Normal file
View file

@ -0,0 +1,39 @@
/* Buttons */
.button-base {
@apply px-4 py-2 bg-white rounded-md border border-gray-300 font-medium text-gray-700 hover:bg-gray-50;
}
.button-sm {
@apply px-3 py-1;
}
.button-danger {
@apply border border-red-300 text-red-500 hover:bg-red-50;
}
.button-primary {
@apply border-0 bg-purple-400 text-white hover:bg-purple-500;
}
/* Form fields */
.input-base {
@apply w-full px-3 py-3 bg-white rounded-md placeholder-gray-400 text-gray-700;
}
/* Custom scrollbars */
.tiny-scrollbar::-webkit-scrollbar {
width: 0.4rem;
height: 0.4rem;
}
.tiny-scrollbar::-webkit-scrollbar-thumb {
border-radius: 0.25rem;
@apply bg-gray-400;
}
.tiny-scrollbar::-webkit-scrollbar-track {
@apply bg-gray-100;
}

View file

@ -0,0 +1,41 @@
/* Elixir HTML-ized inspect result */
.elixir-inspect .atom {
color: #61afef;
}
.elixir-inspect .binary {
color: #5c6370;
}
.elixir-inspect .boolean {
color: #c678dd;
}
.elixir-inspect .list {
color: #5c6370;
}
.elixir-inspect .map {
color: #5c6370;
}
.elixir-inspect .nil {
color: #c678dd;
}
.elixir-inspect .number {
color: #d19a66;
}
.elixir-inspect .regex {
color: #e06c75;
}
.elixir-inspect .string {
color: #98c379;
}
.elixir-inspect .tuple {
color: #5c6370;
}

5
assets/css/global.css Normal file
View file

@ -0,0 +1,5 @@
/* Remove the default outline on focused elements */
:focus,
button:focus {
outline: none;
}

View file

@ -1,4 +1,9 @@
/* LiveView specific classes for your customizations */ /* Hide Phoenix live reload frame */
iframe[hidden] {
display: none;
}
/* LiveView specific classes */
.phx-no-feedback.invalid-feedback, .phx-no-feedback.invalid-feedback,
.phx-no-feedback .invalid-feedback { .phx-no-feedback .invalid-feedback {
@ -13,75 +18,7 @@
.phx-disconnected { .phx-disconnected {
cursor: wait; cursor: wait;
} }
.phx-disconnected * { .phx-disconnected * {
pointer-events: none; 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: rgb(0, 0, 0);
background-color: rgba(0, 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: bold;
}
.phx-modal-close:hover,
.phx-modal-close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
/* Alerts and form errors */
.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;
}
.alert p {
margin-bottom: 0;
}
.alert:empty {
display: none;
}
.invalid-feedback {
color: #a94442;
display: block;
margin: -1rem 0 2rem;
}

99
assets/css/markdown.css Normal file
View file

@ -0,0 +1,99 @@
/* Markdown rendered content */
.markdown {
@apply text-gray-700;
}
.markdown h1 {
@apply text-gray-900 font-semibold text-3xl my-4;
}
.markdown h2 {
@apply text-gray-900 font-semibold text-2xl my-4;
}
.markdown h3 {
@apply text-gray-900 font-semibold text-xl my-4;
}
.markdown p {
@apply my-4;
}
.markdown ul {
@apply list-disc list-inside my-4;
}
.markdown ol {
@apply list-decimal list-inside my-4;
}
.markdown ul > li,
.markdown ol > li {
@apply my-1;
}
.markdown ul > li ul,
.markdown ol > li ol {
@apply ml-6;
}
.markdown blockquote {
@apply border-l-4 border-gray-200 pl-4 py-2 my-4 text-gray-500;
}
.markdown a {
@apply font-medium underline text-gray-900 hover:no-underline;
}
.markdown table {
@apply w-full my-4;
}
.markdown table thead tr {
@apply border-b border-gray-200;
}
.markdown table tbody tr:not(:last-child) {
@apply border-b border-gray-200;
}
.markdown table th {
@apply p-2 font-bold;
}
.markdown table td {
@apply p-2;
}
.markdown table th:first-child,
.markdown table td:first-child {
@apply pl-0;
}
.markdown table th:last-child,
.markdown table td:last-child {
@apply pr-0;
}
.markdown code {
@apply py-1 px-2 rounded-md text-sm align-middle;
/* Match the editor colors */
background-color: #282c34;
color: #abb2bf;
}
.markdown pre > code {
@apply block p-4 rounded-md text-sm align-middle;
/* Match the editor colors */
background-color: #282c34;
color: #abb2bf;
}
.markdown :first-child {
@apply mt-0;
}
.markdown :last-child {
@apply mb-0;
}

View file

@ -1,633 +0,0 @@
/* Includes some default style for the starter application.
* This can be safely deleted to start fresh.
*/
/* Milligram v1.3.0 https://milligram.github.io
* Copyright (c) 2017 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", "Arial", sans-serif;
font-size: 1.6em;
font-weight: 300;
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="email"],
input[type="number"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="text"],
input[type="url"],
textarea,
select {
-webkit-appearance: none;
-moz-appearance: none;
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;
width: 100%;
}
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,
textarea:focus,
select: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;
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;
}
.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-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 {
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;
width: 100%;
}
td,
th {
border-bottom: 0.1rem solid #e1e1e1;
padding: 1.2rem 1.5rem;
text-align: left;
}
td:first-child,
th:first-child {
padding-left: 0;
}
td:last-child,
th:last-child {
padding-right: 0;
}
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;
}
}

10
assets/css/utilities.css Normal file
View file

@ -0,0 +1,10 @@
/* A set of reusable classes */
.bg-editor {
background-color: #282c34;
}
.font-editor {
font-family: "Droid Sans Mono", monospace, monospace, "Droid Sans Fallback";
font-size: 14px;
}

View file

@ -1,135 +0,0 @@
import monaco from "../cell/live_editor/monaco";
import EditorClient from "../cell/live_editor/editor_client";
import MonacoEditorAdapter from "../cell/live_editor/monaco_editor_adapter";
import HookServerAdapter from "./hook_server_adapter";
import {
getAttributeOrThrow,
parseBoolean,
parseInteger,
} from "../lib/attribute";
/**
* A hook managing an editable cell.
*
* Mounts a Monaco Editor and provides real-time collaboration mechanism
* by sending all changes as `Delta` objects to the server
* and handling such objects sent by other clients.
*
* Configuration:
*
* * `data-cell-id` - id of the cell being edited
* * `data-type` - editor type (i.e. language), either "markdown" or "elixir" is expected
* * `data-hidden` - whether this editor is currently hidden
* * `data-active` - whether this editor is currently the active one
*
* Additionally the root element should have a direct `div` child
* with `data-source` and `data-revision` providing the initial values.
*/
const Editor = {
mounted() {
this.props = getProps(this);
this.editorContainer = this.el.querySelector("div");
if (!this.editorContainer) {
throw new Error("Editor Hook root element should have a div child");
}
// Remove the content placeholder
this.editorContainer.firstElementChild.remove();
this.__mountEditor();
const source = getAttributeOrThrow(this.editorContainer, "data-source");
const revision = getAttributeOrThrow(
this.editorContainer,
"data-revision",
parseInteger
);
this.editor.getModel().setValue(source);
new EditorClient(
new HookServerAdapter(this, this.props.cellId),
new MonacoEditorAdapter(this.editor),
revision
);
},
updated() {
const prevProps = this.props;
this.props = getProps(this);
if (prevProps.isHidden && !this.props.isHidden) {
// If the editor was created as hidden it didn't get the chance
// to properly adjust to the available space, so trigger it now.
this.__adjustEditorLayout();
}
if (!prevProps.isActive && this.props.isActive) {
this.editor.focus();
}
if (prevProps.isActive && !this.props.isActive) {
if (this.editor.hasTextFocus()) {
document.activeElement.blur();
}
}
},
__mountEditor() {
this.editor = monaco.editor.create(this.editorContainer, {
language: this.props.type,
value: "",
scrollbar: {
vertical: "hidden",
handleMouseWheel: false,
},
minimap: {
enabled: false,
},
overviewRulerLanes: 0,
scrollBeyondLastLine: false,
quickSuggestions: false,
renderIndentGuides: false,
occurrencesHighlight: false,
renderLineHighlight: "none",
theme: "custom",
});
this.editor.getModel().updateOptions({
tabSize: 2,
});
this.editor.updateOptions({
autoIndent: true,
tabSize: 2,
formatOnType: true,
});
this.editor.onDidContentSizeChange(() => this.__adjustEditorLayout());
this.__adjustEditorLayout();
window.addEventListener("resize", (event) => {
this.editor.layout();
});
},
__adjustEditorLayout() {
// Dynamically adjust editor height to the content, see https://github.com/microsoft/monaco-editor/issues/794
const contentHeight = this.editor.getContentHeight();
this.editorContainer.style.height = `${contentHeight}px`;
this.editor.layout();
},
};
function getProps(hook) {
return {
cellId: getAttributeOrThrow(hook.el, "data-cell-id"),
type: getAttributeOrThrow(hook.el, "data-type"),
isHidden: getAttributeOrThrow(hook.el, "data-hidden", parseBoolean),
isActive: getAttributeOrThrow(hook.el, "data-active", parseBoolean),
};
}
export default Editor;

View file

@ -9256,6 +9256,17 @@
} }
} }
}, },
"postcss-import": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.0.tgz",
"integrity": "sha512-gFDDzXhqr9ELmnLHgCC3TbGfA6Dm/YMb/UN8/f7Uuq4fL7VTk2vOIj6hwINEwbokEmp123bLD7a5m+E+KIetRg==",
"dev": true,
"requires": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
"resolve": "^1.1.7"
}
},
"postcss-js": { "postcss-js": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz",
@ -10711,6 +10722,23 @@
"integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==",
"dev": true "dev": true
}, },
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
"dev": true,
"requires": {
"pify": "^2.3.0"
},
"dependencies": {
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
}
}
},
"readable-stream": { "readable-stream": {
"version": "2.3.7", "version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",

View file

@ -31,6 +31,7 @@
"monaco-editor-webpack-plugin": "^2.1.0", "monaco-editor-webpack-plugin": "^2.1.0",
"optimize-css-assets-webpack-plugin": "^5.0.1", "optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss": "^8.2.3", "postcss": "^8.2.3",
"postcss-import": "^14.0.0",
"postcss-loader": "^4.1.0", "postcss-loader": "^4.1.0",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"tailwindcss": "^2.0.2", "tailwindcss": "^2.0.2",

View file

@ -1,5 +1,6 @@
module.exports = { module.exports = {
plugins: [ plugins: [
require('postcss-import'),
require('tailwindcss'), require('tailwindcss'),
require('autoprefixer'), require('autoprefixer'),
] ]

View file

@ -1,15 +1,15 @@
defmodule LiveBookWeb.Cell do defmodule LiveBookWeb.CellComponent do
use LiveBookWeb, :live_component use LiveBookWeb, :live_component
def render(assigns) do def render(assigns) do
~L""" ~L"""
<div id="cell-<%= @cell.id %>" <div class="flex flex-col relative mr-10 border-l-4 pl-4 -ml-4 border-blue-100 border-opacity-0 hover:border-opacity-100 <%= if @focused, do: "border-blue-300 border-opacity-100"%>"
phx-hook="Cell" id="cell-<%= @cell.id %>"
data-cell-id="<%= @cell.id %>" phx-hook="Cell"
data-type="<%= @cell.type %>" data-cell-id="<%= @cell.id %>"
data-focused="<%= @focused %>" data-type="<%= @cell.type %>"
data-expanded="<%= @expanded %>" data-focused="<%= @focused %>"
class="flex flex-col relative mr-10 border-l-4 pl-4 -ml-4 border-blue-100 border-opacity-0 hover:border-opacity-100 <%= if @focused, do: "border-blue-300 border-opacity-100"%>"> data-expanded="<%= @expanded %>">
<%= render_cell_content(assigns) %> <%= render_cell_content(assigns) %>
</div> </div>
""" """
@ -64,9 +64,10 @@ defmodule LiveBookWeb.Cell do
~L""" ~L"""
<div class="py-3 rounded-md overflow-hidden bg-editor relative"> <div class="py-3 rounded-md overflow-hidden bg-editor relative">
<div id="editor-container-<%= @cell.id %>" <div
data-editor-container id="editor-container-<%= @cell.id %>"
phx-update="ignore"> data-editor-container
phx-update="ignore">
<%= render_editor_content_placeholder(@cell.source) %> <%= render_editor_content_placeholder(@cell.source) %>
</div> </div>
@ -97,9 +98,9 @@ defmodule LiveBookWeb.Cell do
~L""" ~L"""
<div class="max-w-2xl w-full animate-pulse"> <div class="max-w-2xl w-full animate-pulse">
<div class="flex-1 space-y-4"> <div class="flex-1 space-y-4">
<div class="h-4 bg-gray-200 rounded w-3/4"></div> <div class="h-4 bg-gray-200 rounded-md w-3/4"></div>
<div class="h-4 bg-gray-200 rounded"></div> <div class="h-4 bg-gray-200 rounded-md"></div>
<div class="h-4 bg-gray-200 rounded w-5/6"></div> <div class="h-4 bg-gray-200 rounded-md w-5/6"></div>
</div> </div>
</div> </div>
""" """
@ -119,9 +120,9 @@ defmodule LiveBookWeb.Cell do
~L""" ~L"""
<div class="px-8 max-w-2xl w-full animate-pulse"> <div class="px-8 max-w-2xl w-full animate-pulse">
<div class="flex-1 space-y-4 py-1"> <div class="flex-1 space-y-4 py-1">
<div class="h-4 bg-gray-500 rounded w-3/4"></div> <div class="h-4 bg-gray-500 rounded-md w-3/4"></div>
<div class="h-4 bg-gray-500 rounded"></div> <div class="h-4 bg-gray-500 rounded-md"></div>
<div class="h-4 bg-gray-500 rounded w-5/6"></div> <div class="h-4 bg-gray-500 rounded-md w-5/6"></div>
</div> </div>
</div> </div>
""" """

View file

@ -1,24 +1,22 @@
defmodule LiveBookWeb.InsertCellActions do defmodule LiveBookWeb.InsertCellComponent do
use LiveBookWeb, :live_component use LiveBookWeb, :live_component
def render(assigns) do def render(assigns) do
~L""" ~L"""
<div class="opacity-0 hover:opacity-100 flex space-x-2 justify-center items-center"> <div class="opacity-0 hover:opacity-100 flex space-x-2 justify-center items-center">
<%= line() %> <%= line() %>
<button <button class="py-1 px-2 rounded-md text-sm hover:bg-gray-100 border border-gray-200 bg-gray-50"
phx-click="insert_cell" phx-click="insert_cell"
phx-value-type="markdown" phx-value-type="markdown"
phx-value-section_id="<%= @section_id %>" phx-value-section_id="<%= @section_id %>"
phx-value-index="<%= @index %>" phx-value-index="<%= @index %>">
class="py-1 px-2 rounded text-sm hover:bg-gray-100 border border-gray-200 bg-gray-50">
+ Markdown + Markdown
</button> </button>
<button <button class="py-1 px-2 rounded-md text-sm hover:bg-gray-100 border border-gray-200 bg-gray-50"
phx-click="insert_cell" phx-click="insert_cell"
phx-value-type="elixir" phx-value-type="elixir"
phx-value-section_id="<%= @section_id %>" phx-value-section_id="<%= @section_id %>"
phx-value-index="<%= @index %>" phx-value-index="<%= @index %>">
class="py-1 px-2 rounded text-sm hover:bg-gray-100 border border-gray-200 bg-gray-50">
+ Elixir + Elixir
</button> </button>
<%= line() %> <%= line() %>

View file

@ -91,16 +91,11 @@ defmodule LiveBookWeb.RuntimeComponent do
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
Then enter the name of the node below: Then enter the name of the node below:
</p> </p>
<%= f = form_for :node, "#", <%= f = form_for :node, "#", phx_target: @myself, phx_submit: "init_attached" %>
phx_target: @myself, <%= text_input f, :name, class: "input-base text-sm shadow",
phx_submit: "init_attached" %> placeholder: if(LiveBook.Config.shortnames?, do: "test", else: "test@127.0.0.1") %>
<%= text_input f, :name, <%= submit "Connect", class: "mt-3 button-base text-sm" %>
placeholder: if(LiveBook.Config.shortnames?, do: "test", else: "test@127.0.0.1"),
class: "input-base text-sm shadow" %>
<%= submit "Connect",
class: "mt-3 button-base text-sm" %>
</form> </form>
</div> </div>
</div> </div>

View file

@ -1,48 +0,0 @@
defmodule LiveBookWeb.Section do
use LiveBookWeb, :live_component
def render(assigns) do
~L"""
<div class="<%= if not @selected, do: "hidden" %>">
<div class="flex justify-between items-center">
<div class="flex space-x-2 items-center text-gray-600">
<%= Icons.svg(:chevron_right, class: "h-8") %>
<h2 id="section-<%= @section.id %>-name"
contenteditable
spellcheck="false"
phx-blur="set_section_name"
phx-value-section_id="<%= @section.id %>"
phx-hook="ContentEditable"
data-update-attribute="phx-value-name"
class="text-3xl"><%= @section.name %></h2>
</div>
<div class="flex space-x-2 items-center">
<button phx-click="delete_section" phx-value-section_id="<%= @section.id %>" class="text-gray-600 hover:text-current">
<%= Icons.svg(:trash, class: "h-6") %>
</button>
</div>
</div>
<div class="container py-4">
<div class="flex flex-col space-y-2 pb-80">
<%= live_component @socket, LiveBookWeb.InsertCellActions,
id: "#{@section.id}:0",
section_id: @section.id,
index: 0 %>
<%= for {cell, index} <- Enum.with_index(@section.cells) do %>
<%= live_component @socket, LiveBookWeb.Cell,
id: cell.id,
cell: cell,
cell_info: @cell_infos[cell.id],
focused: @selected and cell.id == @focused_cell_id,
expanded: @selected and cell.id == @focused_cell_id and @focused_cell_expanded %>
<%= live_component @socket, LiveBookWeb.InsertCellActions,
id: "#{@section.id}:#{index + 1}",
section_id: @section.id,
index: index + 1 %>
<% end %>
</div>
</div>
</div>
"""
end
end

View file

@ -0,0 +1,50 @@
defmodule LiveBookWeb.SectionComponent do
use LiveBookWeb, :live_component
def render(assigns) do
~L"""
<div class="<%= if not @selected, do: "hidden" %>">
<div class="flex justify-between items-center">
<div class="flex space-x-2 items-center text-gray-600">
<%= Icons.svg(:chevron_right, class: "h-8") %>
<h2 class="text-3xl"
id="section-<%= @section.id %>-name"
contenteditable
spellcheck="false"
phx-blur="set_section_name"
phx-value-section_id="<%= @section.id %>"
phx-hook="ContentEditable"
data-update-attribute="phx-value-name"><%= @section.name %></h2>
<%# ^ Note it's important there's no space between <h2> and </h2>
because we want the content to exactly match @section.name. %>
</div>
<div class="flex space-x-2 items-center">
<button phx-click="delete_section" phx-value-section_id="<%= @section.id %>" class="text-gray-600 hover:text-current">
<%= Icons.svg(:trash, class: "h-6") %>
</button>
</div>
</div>
<div class="container py-4">
<div class="flex flex-col space-y-2 pb-80">
<%= live_component @socket, LiveBookWeb.InsertCellComponent,
id: "#{@section.id}:0",
section_id: @section.id,
index: 0 %>
<%= for {cell, index} <- Enum.with_index(@section.cells) do %>
<%= live_component @socket, LiveBookWeb.CellComponent,
id: cell.id,
cell: cell,
cell_info: @cell_infos[cell.id],
focused: @selected and cell.id == @focused_cell_id,
expanded: @selected and cell.id == @focused_cell_id and @focused_cell_expanded %>
<%= live_component @socket, LiveBookWeb.InsertCellComponent,
id: "#{@section.id}:#{index + 1}",
section_id: @section.id,
index: index + 1 %>
<% end %>
</div>
</div>
</div>
"""
end
end

View file

@ -18,7 +18,7 @@ defmodule LiveBookWeb.SessionLive do
{:ok, assign(socket, initial_assigns(session_id, data))} {:ok, assign(socket, initial_assigns(session_id, data))}
else else
{:ok, redirect(socket, to: Routes.live_path(socket, LiveBookWeb.SessionsLive))} {:ok, redirect(socket, to: Routes.sessions_path(socket, :page))}
end end
end end
@ -44,11 +44,11 @@ defmodule LiveBookWeb.SessionLive do
~L""" ~L"""
<%= if @live_action == :runtime do %> <%= if @live_action == :runtime do %>
<%= live_modal @socket, LiveBookWeb.RuntimeComponent, <%= live_modal @socket, LiveBookWeb.RuntimeComponent,
id: :runtime_modal, id: :runtime_modal,
action: :runtime, action: :runtime,
return_to: Routes.session_path(@socket, :show, @session_id), return_to: Routes.session_path(@socket, :page, @session_id),
session_id: @session_id, session_id: @session_id,
runtime: @data.runtime %> runtime: @data.runtime %>
<% end %> <% end %>
<div class="flex flex-grow h-full" <div class="flex flex-grow h-full"
@ -91,13 +91,13 @@ defmodule LiveBookWeb.SessionLive do
<div class="flex-grow px-6 py-8 flex overflow-y-auto"> <div class="flex-grow px-6 py-8 flex overflow-y-auto">
<div class="max-w-screen-lg w-full mx-auto"> <div class="max-w-screen-lg w-full mx-auto">
<%= for section <- @data.notebook.sections do %> <%= for section <- @data.notebook.sections do %>
<%= live_component @socket, LiveBookWeb.Section, <%= live_component @socket, LiveBookWeb.SectionComponent,
id: section.id, id: section.id,
section: section, section: section,
selected: section.id == @selected_section_id, selected: section.id == @selected_section_id,
cell_infos: @data.cell_infos, cell_infos: @data.cell_infos,
focused_cell_id: @focused_cell_id, focused_cell_id: @focused_cell_id,
focused_cell_expanded: @focused_cell_expanded %> focused_cell_expanded: @focused_cell_expanded %>
<% end %> <% end %>
</div> </div>
</div> </div>

View file

@ -16,14 +16,14 @@ defmodule LiveBookWeb.SessionsLive do
def render(assigns) do def render(assigns) do
~L""" ~L"""
<div class="container max-w-screen-md p-4 mx-auto"> <div class="container max-w-screen-md p-4 mx-auto">
<div class="flex flex-col shadow-md rounded px-3 py-2 mb-4"> <div class="flex flex-col shadow-md rounded-md px-3 py-2 mb-4">
<div class="text-gray-700 text-lg font-semibold p-2"> <div class="text-gray-700 text-lg font-semibold p-2">
Sessions Sessions
</div> </div>
<%= for session_id <- Enum.sort(@session_ids) do %> <%= for session_id <- Enum.sort(@session_ids) do %>
<div class="p-3 flex"> <div class="p-3 flex">
<div class="flex-grow text-lg text-gray-500 hover:text-current"> <div class="flex-grow text-lg text-gray-500 hover:text-current">
<%= live_redirect session_id, to: Routes.session_path(@socket, :show, session_id) %> <%= live_redirect session_id, to: Routes.session_path(@socket, :page, session_id) %>
</div> </div>
<div> <div>
<button phx-click="delete_session" phx-value-id="<%= session_id %>" aria-label="delete" class="text-gray-500 hover:text-current"> <button phx-click="delete_session" phx-value-id="<%= session_id %>" aria-label="delete" class="text-gray-500 hover:text-current">
@ -44,7 +44,7 @@ defmodule LiveBookWeb.SessionsLive do
def handle_event("create_session", _params, socket) do def handle_event("create_session", _params, socket) do
case LiveBook.SessionSupervisor.create_session() do case LiveBook.SessionSupervisor.create_session() do
{:ok, id} -> {:ok, id} ->
{:noreply, push_redirect(socket, to: Routes.session_path(socket, :show, id))} {:noreply, push_redirect(socket, to: Routes.session_path(socket, :page, id))}
{:error, reason} -> {:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to create a notebook: #{reason}")} {:noreply, put_flash(socket, :error, "Failed to create a notebook: #{reason}")}

View file

@ -17,9 +17,9 @@ defmodule LiveBookWeb.Router do
scope "/", LiveBookWeb do scope "/", LiveBookWeb do
pipe_through :browser pipe_through :browser
live "/", HomeLive live "/", HomeLive, :page
live "/sessions", SessionsLive live "/sessions", SessionsLive, :page
live "/sessions/:id", SessionLive, :show live "/sessions/:id", SessionLive, :page
live "/sessions/:id/runtime", SessionLive, :runtime live "/sessions/:id/runtime", SessionLive, :runtime
end end

View file

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<%= csrf_meta_tag() %> <%= csrf_meta_tag() %>
<%= live_title_tag assigns[:page_title] || "LiveBook", suffix: " · Phoenix Framework" %> <%= live_title_tag assigns[:page_title] || "LiveBook" %>
<link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/> <link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script defer phx-track-static type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script> <script defer phx-track-static type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head> </head>