This commit is contained in:
44
app/views/accounts/custom_styles/edit.html.erb
Normal file
44
app/views/accounts/custom_styles/edit.html.erb
Normal file
@@ -0,0 +1,44 @@
|
||||
<% content_for(:title) { "Custom styles" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to users_path, class: "btn" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Go back</span>
|
||||
<% end %>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= image_tag "art.svg", aria: { label: "Custom styles" }, size: 19, class: "colorize--black", alt: "Custom styles" %>
|
||||
</div>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="center pad txt-align-center flex flex-column">
|
||||
<%= form_with model: @account, url: account_custom_styles_url, class: "flex flex-column gap",
|
||||
data: { controller: "form", action: "keydown.ctrl+enter->form#submit keydown.meta+enter->form#submit" } do |form| %>
|
||||
<div class="pad-inline-double margin-inline">
|
||||
<div class="flex align-center gap-half center full-width justify-center">
|
||||
<span class="txt-medium"><%= translation_button(:custom_styles) %></span>
|
||||
<h1 class="margin-none">Custom CSS</h1>
|
||||
</div>
|
||||
<p class="flex flex-wrap align-center justify-center gap margin-none-block-start" style="--column-gap: 0.5ch; --row-gap: 0">
|
||||
<span>Add custom CSS styles.</span>
|
||||
<%= image_tag "alert.svg", class: "flex-inline colorize--black", size: 16, aria: { hidden: "true" } %>
|
||||
<span>Use Caution: you could break things.</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label class="flex align-start gap flex-item-grow">
|
||||
<%= form.text_area :custom_styles, class: "input input--code txt--small", placeholder: "Add CSS styles…",
|
||||
autocomplete: "off", spellcheck: "false", autocorrect: "off", autocapitalize: "off",
|
||||
rows: 16, required: false %>
|
||||
</label>
|
||||
|
||||
<%= form.button class: "btn btn--reversed center txt-large", type: "submit" do %>
|
||||
<%= image_tag "check.svg", aria: { hidden: "true" }, size: 20 %>
|
||||
<span class="for-screen-reader">Save changes</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
json.message "File uploaded successfully"
|
||||
json.fileName @upload.filename.to_s
|
||||
json.mimetype @upload.content_type
|
||||
json.fileUrl @upload.slug_url host: request.host
|
||||
21
app/views/books/_book.html.erb
Normal file
21
app/views/books/_book.html.erb
Normal file
@@ -0,0 +1,21 @@
|
||||
<% cache book do %>
|
||||
<figure class="library__book <%= "theme--#{book&.theme}" unless book.cover.attached? %>">
|
||||
<div class="flex flex-column gap-half">
|
||||
<div class="flex-inline position-relative center">
|
||||
<% if book.cover.attached? %>
|
||||
<%= image_tag book.cover, alt: "Book cover", class: "book__cover" %>
|
||||
<% else %>
|
||||
<span class="book__cover-wrapper">
|
||||
<%= image_tag "empty-cover.png", alt: "Book cover", class: "book__cover margin-block-none center" %>
|
||||
<span class="book__title overflow-line-clamp pad txt-align-start txt-tight-lines" style="--lines: 6" aria-hidden="true"><%= book.title %></span>
|
||||
</span>
|
||||
<% end %>
|
||||
<%= turbo_frame_tag dom_id(book, :bookmark), src: book_bookmark_path(book), target: "_top" %>
|
||||
</div>
|
||||
<h2 class="margin-none flex flex-column txt-normal txt-tight-lines txt-medium--responsive">
|
||||
<strong><%= book.title %></strong>
|
||||
<span class="overflow-line-clamp"><%= book.author %></span>
|
||||
</h2>
|
||||
</div>
|
||||
</figure>
|
||||
<% end %>
|
||||
31
app/views/books/_create_buttons.html.erb
Normal file
31
app/views/books/_create_buttons.html.erb
Normal file
@@ -0,0 +1,31 @@
|
||||
<%= book_part_create_button book, Page do %>
|
||||
<svg viewBox="0 0 20 24" xmlns="http://www.w3.org/2000/svg" fill="var(--color-ink)">
|
||||
<path d="m15.8 21.7c0 .3-.2.4-.4.4h-13.5-.2v-16.9c0-.3.2-.4.4-.4h6.3c0-.6.1-1.2.3-1.8h-6.9c-1 0-1.8.8-1.8 1.8v17.5c0 1 .8 1.8 1.8 1.8h14c.9 0 1.6-.6 1.8-1.4v-11.6c-.5.2-1.1.4-1.8.5v10.3z"/>
|
||||
<path fill="var(--color-positive)" d="m15 0c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm1.9 5.6h-1.2c-.1 0-.2 0-.2.2v1.2c0 .3-.3.6-.6.6s-.6-.3-.6-.6v-1.2c0-.1 0-.2-.2-.2h-1.2c-.3 0-.6-.3-.6-.6s.3-.6.6-.6h1.2c.1 0 .2 0 .2-.2v-1.2c0-.3.3-.6.6-.6s.6.3.6.6v1.2c0 .1 0 .2.2.2h1.2c.3 0 .6.3.6.6s-.3.6-.6.6z"/>
|
||||
<path d="m4.4 16.3.4-.4c.3-.3.8-.3 1.1 0 1 .9 2.5.9 3.4 0 .3-.3.8-.3 1.1 0 1 1 2.5 1 3.4 0l.3-.3c.3-.3.3-.8 0-1.1s-.8-.3-1.1 0l-.3.3c-.3.3-.8.3-1.1 0-1-1-2.5-1-3.4 0-.3.3-.8.3-1.1 0-1-1-2.5-1-3.4 0l-.4.4c-.3.3-.3.8 0 1.1s.8.3 1.1 0z"/>
|
||||
<path d="m4.5 20.1.5-.5c.4-.4.9-.4 1.3 0 1.1 1.1 2.8 1.1 3.9 0 .3-.4.3-.9 0-1.3-.4-.3-.9-.3-1.3 0s-.9.3-1.3 0c-1.1-1.1-2.8-1.1-3.9 0l-.5.5c-.3.4-.3.9 0 1.3.4.3.9.3 1.3 0z"/>
|
||||
<path d="m12.8 11.2c-.3.3-.8.3-1.1 0-1-1-2.5-1-3.4 0-.3.3-.8.3-1.1 0-1-1-2.5-1-3.4 0s-.4.4-.4.4c-.3.3-.3.8 0 1.1s.8.3 1.1 0 .4-.4.4-.4c.3-.3.8-.3 1.1 0 1 .9 2.5.9 3.4 0 .3-.3.8-.3 1.1 0 1 1 2.5 1 3.4 0s.3-.3.3-.3c.2-.2.3-.4.2-.6-.6 0-1.1-.2-1.6-.3z"/>
|
||||
<path d="m8.3 7.6c-.3.3-.8.3-1.1 0-1-1-2.5-1-3.4 0l-.4.4c-.3.3-.3.8 0 1.1s.8.3 1.1 0 .4-.4.4-.4c.3-.3.8-.3 1.1 0 1 .9 2.5.9 3.4 0 0 0 .1-.1.2-.1-.3-.4-.5-.9-.7-1.4-.2.1-.4.3-.6.4z"/>
|
||||
</svg>
|
||||
<span class="for-screen-reader">Add a new text page</span>
|
||||
<% end %>
|
||||
|
||||
<%= book_part_create_button book, Picture do %>
|
||||
<svg viewBox="0 0 20 24" xmlns="http://www.w3.org/2000/svg" fill="var(--color-ink)">
|
||||
<path d="m14.4 16.8s.2-.2.2-.3v-5.1c-2.8-.2-5.1-2.2-5.8-4.9h-4.2c-.8 0-1.4.6-1.4 1.4v11.1c0 .8.6 1.5 1.5 1.5h1.2s.2 0 .3-.2l4.4-6.8c.2-.3.3-.4.7-.4s.5.2.7.4l2.2 3.1c0 .2.3.2.4 0zm-7.3-1.9c-.9 0-1.7-.8-1.7-1.7s.8-1.7 1.7-1.7 1.7.8 1.7 1.7-.8 1.7-1.7 1.7z"/>
|
||||
<path d="m11.2 15.8c0-.2-.2-.2-.3 0s-2.6 4.1-2.6 4.1v.4h.2 4.3c.3 0 .7-.2.8-.3s.2-.2 0-.3l-2.4-3.8z"/>
|
||||
<path d="m15.8 21.7c0 .3-.2.4-.4.4h-13.5-.2v-16.9c0-.3.2-.4.4-.4h6.3c0-.6.1-1.2.3-1.8h-6.9c-1 0-1.8.8-1.8 1.8v17.5c0 1 .8 1.8 1.8 1.8h14c.9 0 1.6-.6 1.8-1.4v-11.6c-.5.2-1.1.4-1.8.5v10.3z"/>
|
||||
<path fill="var(--color-positive)" d="m15 0c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm1.9 5.6h-1.2c-.1 0-.2 0-.2.2v1.2c0 .3-.3.6-.6.6s-.6-.3-.6-.6v-1.2c0-.1 0-.2-.2-.2h-1.2c-.3 0-.6-.3-.6-.6s.3-.6.6-.6h1.2c.1 0 .2 0 .2-.2v-1.2c0-.3.3-.6.6-.6s.6.3.6.6v1.2c0 .1 0 .2.2.2h1.2c.3 0 .6.3.6.6s-.3.6-.6.6z"/>
|
||||
</svg>
|
||||
<span class="for-screen-reader">Add a new picture page</span>
|
||||
<% end %>
|
||||
|
||||
<%= book_part_create_button book, Section do %>
|
||||
<svg viewBox="0 0 20 24" xmlns="http://www.w3.org/2000/svg" fill="var(--color-ink)">
|
||||
<path d="m15 11.5c-3.6 0-6.5-2.9-6.5-6.5s0-.2 0-.2h-6.3c-.3 0-.4.2-.4.4v16.9s0 .2.2 0h13.4c.3 0 .4-.2.4-.4v-10.3c-.2 0-.5 0-.8 0z" opacity="0"/>
|
||||
<path d="m15.8 21.7c0 .3-.2.4-.4.4h-13.5-.2v-16.9c0-.3.2-.4.4-.4h6.3c0-.6.1-1.2.3-1.8h-6.9c-1 0-1.8.8-1.8 1.8v17.5c0 1 .8 1.8 1.8 1.8h14c.9 0 1.6-.6 1.8-1.4v-11.6c-.5.2-1.1.4-1.8.5v10.3z"/>
|
||||
<path fill="var(--color-positive)" d="m15 0c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm1.9 5.6h-1.2c-.1 0-.2 0-.2.2v1.2c0 .3-.3.6-.6.6s-.6-.3-.6-.6v-1.2c0-.1 0-.2-.2-.2h-1.2c-.3 0-.6-.3-.6-.6s.3-.6.6-.6h1.2c.1 0 .2 0 .2-.2v-1.2c0-.3.3-.6.6-.6s.6.3.6.6v1.2c0 .1 0 .2.2.2h1.2c.3 0 .6.3.6.6s-.3.6-.6.6z"/>
|
||||
<path d="m4.4 14.5.4-.4c.3-.3.8-.3 1.1 0 1 .9 2.5.9 3.4 0 .3-.3.8-.3 1.1 0 1 1 2.5 1 3.4 0l.3-.3c.3-.3.3-.8 0-1.1s-.8-.3-1.1 0l-.3.3c-.3.3-.8.3-1.1 0-1-1-2.5-1-3.4 0-.3.3-.8.3-1.1 0-1-1-2.5-1-3.4 0l-.4.4c-.3.3-.3.8 0 1.1s.8.3 1.1 0z"/>
|
||||
</svg>
|
||||
<span class="for-screen-reader">Add a new section page</span>
|
||||
<% end %>
|
||||
15
app/views/books/_edit_mode.html.erb
Normal file
15
app/views/books/_edit_mode.html.erb
Normal file
@@ -0,0 +1,15 @@
|
||||
<%= tag.div class: "flex align-center gap flex-item-no-shrink",
|
||||
data: {
|
||||
controller: "edit-mode",
|
||||
edit_mode_target_url_value: target_url,
|
||||
edit_mode_editing_class: "edit-mode",
|
||||
edit_mode_autosave_outlet: "[data-controller='autosave']",
|
||||
} do %>
|
||||
<%= image_tag "eye.svg", aria: { hidden: true }, size: 28, class: "colorize--black" %>
|
||||
<label class="switch txt-medium">
|
||||
<%= check_box_tag :edit_mode_enabled, checked: checked, class: "switch__input", data: { action: "edit-mode#change" } %>
|
||||
<span class="switch__btn round"></span>
|
||||
<span class="for-screen-reader">Editing mode</span>
|
||||
</label>
|
||||
<%= image_tag "write.svg", aria: { hidden: true }, size: 24, class: "colorize--black" %>
|
||||
<% end %>
|
||||
85
app/views/books/_form.html.erb
Normal file
85
app/views/books/_form.html.erb
Normal file
@@ -0,0 +1,85 @@
|
||||
<%= form_with model: book, id: "book-editor" do |form| %>
|
||||
<%= tag.div class:"book__form flex align-center gap full-width #{"theme--#{book&.theme}" unless book.cover.attached? }", style:"--input-padding: 0.5rem 1rem; --input-border-radius: 0.5rem" do %>
|
||||
<div class="flex gap-half">
|
||||
<fieldset class="flex flex-column unpad margin-block-end borderless justify-space-between">
|
||||
<legend class="for-screen-reader">Cover color</legend>
|
||||
|
||||
<% Book.themes.keys.each do | theme | %>
|
||||
<label class="btn btn--circle txt-small" style="--btn-background: var(--theme-color--<%= theme -%>)" >
|
||||
<%= form.radio_button :theme, theme %>
|
||||
<%= image_tag "check.svg", aria: { hidden: "true" }, size: 24, class: "checked" %>
|
||||
<span class="for-screen-reader"><%= theme %></span>
|
||||
</label>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
<%= tag.div class: "flex flex-column", data: { controller: "upload-preview", upload_preview_default_image_value: asset_url("empty-cover.png") } do %>
|
||||
<label class="align-center center gap position-relative margin-block-end">
|
||||
<% unless book.cover.attached? %>
|
||||
<span class="btn btn--reversed txt-medium book__cover--add">
|
||||
<%= image_tag "camera.svg", aria: { hidden: "true" }, size: 24 %>
|
||||
<span class="for-screen-reader">Upload a cover</span>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<div class="input--file">
|
||||
<%= image_tag book.cover.attached? ? book.cover : "empty-cover.png", alt: "Book cover",
|
||||
class: "book__cover margin-none", style: "--cover-height: 60vh",
|
||||
data: { upload_preview_target: "image" } %>
|
||||
<%= form.file_field :cover, class: "input", accept: "image/png, image/jpeg, image/jpg, image/webp",
|
||||
data: { upload_preview_target: "input", action: "upload-preview#previewImage" },
|
||||
title: book.cover.attached? ? "Replace book cover" : "Upload book cover" %>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<% if book.cover.attached? %>
|
||||
<%= tag.label class:"btn btn--negative txt-small center book__cover--remove", data: { action: "click->upload-preview#clear", upload_preview_target: "button" } do %>
|
||||
<%= image_tag "minus.svg", aria: { hidden: "true" }, size: 24 %>
|
||||
<%= check_box_tag "remove_cover", "true" %>
|
||||
<span class="for-screen-reader">Remove cover image</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<div class="flex flex-column gap full-width">
|
||||
<div class="flex align-center gap txt-medium">
|
||||
<%= translation_button(:book_title) %>
|
||||
<h1 class="txt-xx-large margin-none full-width">
|
||||
<%= form.text_field :title, required: true, autofocus: true, class: "input", placeholder: "Book title", autocomplete: "off" %>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="flex align-center gap txt-medium">
|
||||
<%= translation_button(:book_subtitle) %>
|
||||
<small class="txt-normal txt-large txt-tight-lines full-width"><%= form.text_area :subtitle, class: "input", placeholder: "Subtitle", autocomplete: "off" %></small>
|
||||
</div>
|
||||
<div class="flex align-center gap txt-medium">
|
||||
<%= translation_button(:book_author) %>
|
||||
<small class="txt-normal txt-large txt-tight-lines full-width"><%= form.text_field :author, class: "input", placeholder: "Author", autocomplete: "off" %></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="book-access border-radius center pad-double fill-shade flex flex-column gap margin-block-double">
|
||||
<div class="flex align-center gap txt-medium--responsive">
|
||||
<%= image_tag "eye.svg", aria: { hidden: true }, size: 36, class: "colorize--black" %>
|
||||
|
||||
<div class="min-width">
|
||||
<div class="overflow-ellipsis fill-shade"><strong>Everyone</strong></div>
|
||||
</div>
|
||||
|
||||
<hr class="flex-item-grow margin-none" aria-hidden="true" style="--border-style: dashed">
|
||||
|
||||
<label for="book_everyone_access" class="switch">
|
||||
<%= form.check_box :everyone_access, class: "switch__input book-access__switch" %>
|
||||
<span class="switch__btn"></span>
|
||||
<span class="for-screen-reader">Only allow some people to read this book</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<hr class="full-width margin-block-start margin-block-end-half">
|
||||
|
||||
<%= render partial: "books/accesses/access", collection: users, as: :user, locals: { book: book, creating_user: creating_user } %>
|
||||
</div>
|
||||
|
||||
<%= form.submit "Save", hidden: true %>
|
||||
<% end %>
|
||||
4
app/views/books/_index_link.html.erb
Normal file
4
app/views/books/_index_link.html.erb
Normal file
@@ -0,0 +1,4 @@
|
||||
<%= link_to root_path, class: "btn borderless txt-small flex-item-no-shrink" do %>
|
||||
<%= image_tag "books.svg", aria: { label: "Books" }, size: 19, class: "colorize--black", alt: "Books" %>
|
||||
<span class="for-screen-reader">All books</span>
|
||||
<% end %>
|
||||
11
app/views/books/_mode_buttons.html.erb
Normal file
11
app/views/books/_mode_buttons.html.erb
Normal file
@@ -0,0 +1,11 @@
|
||||
<label class="btn arrange-mode__button txt-medium flex-item-justify-end disable-when-deleting disable-when-empty">
|
||||
<input type="checkbox" name="arrange-mode" id="arrange-mode" data-action="arrangement#setArrangeMode">
|
||||
<%= image_tag "rearrange.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Rearrange</span>
|
||||
</label>
|
||||
|
||||
<label class="btn delete-mode__button txt-medium disable-when-arranging disable-when-empty">
|
||||
<input type="checkbox" id="delete-mode">
|
||||
<%= image_tag "minus.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Delete pages</span>
|
||||
</label>
|
||||
12
app/views/books/_new.html.erb
Normal file
12
app/views/books/_new.html.erb
Normal file
@@ -0,0 +1,12 @@
|
||||
<figure class="library__book position-relative">
|
||||
<div class="library__book--empty center">
|
||||
<%= image_tag "empty-cover.png", alt: "Book cover", class: "book__cover" %>
|
||||
<span class="btn btn--positive center">
|
||||
<%= image_tag "add.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader"></span>
|
||||
</span>
|
||||
</div>
|
||||
<%= link_to new_book_path, class: "bookmark__link" do %>
|
||||
<span class="for-screen-reader">Create a new book</span>
|
||||
<% end %>
|
||||
</figure>
|
||||
24
app/views/books/accesses/_access.html.erb
Normal file
24
app/views/books/accesses/_access.html.erb
Normal file
@@ -0,0 +1,24 @@
|
||||
<div class="flex align-center gap txt-medium--responsive" data-controller="dependent-checkbox">
|
||||
<span class="flex align-center gap-half">
|
||||
<% if user.can_administer? %>
|
||||
<%= image_tag "crown.svg", size: 18, aria: { hidden: "true" }, class: "colorize--black" %>
|
||||
<% end %>
|
||||
<span class="overflow-ellipsis txt-medium--responsive"><%= user.name %></span>
|
||||
</span>
|
||||
<hr class="flex-item-grow margin-none" aria-hidden="true" style="--border-style: dashed">
|
||||
|
||||
<fieldset class="flex align-center gap borderless unpad margin-none">
|
||||
<legend class="for-screen-reader"><%= user.name %></legend>
|
||||
<label class="btn btn--small flex-item-no-shrink">
|
||||
<%= check_box_tag "editor_ids[]", user.id, book.editable?(user: user) || user == creating_user || user.can_administer?, id: nil, disabled: user.current? || user.can_administer?, data: { action: "dependent-checkbox#input", dependent_checkbox_target: "dependant" }, aria: { label: "Role: Writer" } %>
|
||||
<%= image_tag "write.svg", size: 24, aria: { hidden: "true" } %>
|
||||
<span class="for-screen-reader"></span>
|
||||
</label>
|
||||
|
||||
<label class="btn btn--small flex-item-no-shrink book-access__reader">
|
||||
<%= check_box_tag "reader_ids[]", user.id, book.accessable?(user: user) || user == creating_user || user.can_administer?, id: nil, disabled: user.current? || user.can_administer?, data: { action: "dependent-checkbox#input", dependent_checkbox_target: "dependee" }, aria: { label: "Role: Reader" } %>
|
||||
<%= image_tag "eye.svg", size: 24, aria: { hidden: "true" } %>
|
||||
<span class="for-screen-reader"></span>
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
4
app/views/books/accesses/_editor.html.erb
Normal file
4
app/views/books/accesses/_editor.html.erb
Normal file
@@ -0,0 +1,4 @@
|
||||
<%= button_to book_user_access_path(book, user), method: :delete, class: "btn txt-small flex-item-no-shrink" do %>
|
||||
<%= image_tag "write.svg", size: 24, aria: { hidden: "true" } %>
|
||||
<span class="for-screen-reader">Role: Writer</span>
|
||||
<% end %>
|
||||
4
app/views/books/accesses/_none.html.erb
Normal file
4
app/views/books/accesses/_none.html.erb
Normal file
@@ -0,0 +1,4 @@
|
||||
<%= button_to book_user_access_path(book, user), params: { level: :reader }, class: "btn txt-small flex-item-no-shrink" do %>
|
||||
<%= image_tag "remove.svg", size: 24, aria: { hidden: "true" } %>
|
||||
<span class="for-screen-reader">Role: None</span>
|
||||
<% end %>
|
||||
4
app/views/books/accesses/_reader.html.erb
Normal file
4
app/views/books/accesses/_reader.html.erb
Normal file
@@ -0,0 +1,4 @@
|
||||
<%= button_to book_user_access_path(book, user), params: { level: :editor }, class: "btn txt-small flex-item-no-shrink" do %>
|
||||
<%= image_tag "eye.svg", size: 24, aria: { hidden: "true" } %>
|
||||
<span class="for-screen-reader">Role: Reader</span>
|
||||
<% end %>
|
||||
21
app/views/books/bookmarks/show.html.erb
Normal file
21
app/views/books/bookmarks/show.html.erb
Normal file
@@ -0,0 +1,21 @@
|
||||
<%= turbo_frame_tag dom_id(@book, :bookmark) do %>
|
||||
<% if @leaf %>
|
||||
<%= link_to book_slug_path(@book, anchor: dom_id(@leaf)), class: "bookmark__link" do %>
|
||||
<span class="for-screen-reader">Bookmark: Resume reading <%= @book.title %></span>
|
||||
<% end %>
|
||||
|
||||
<%= tag.span class: "bookmark", style: "--progress: #{ @leaf.position_as_percentage }%" do %>
|
||||
<div class="flex align-center">
|
||||
<span class="bookmark__icon">
|
||||
<svg viewBox="0 0 64 64" width="64px" height="64px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m17.01 54.01v-42.98.00000045c-.00000025-1.65685 1.34315-3 3-3h24-.00000013c1.65685-.00000007 3 1.34315 3 3v42.98.0001206c0 1.10457-.895431 2-2 2-.34994 0-.693756-.0918173-.997105-.266281l-11.5039-6.61564c-.308638-.17748-.688362-.17748-.997 0l-11.5046 6.6153.00000011-.00000006c-.957413.550849-2.1801.221264-2.73095-.736149-.174564-.303405-.266442-.647312-.266451-.997351z" fill-rule="evenodd" fill="var(--color-marker)" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to book_slug_path(@book), class: "bookmark__link" do %>
|
||||
<span class="for-screen-reader">Start reading <%= @book.title %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
25
app/views/books/edit.html.erb
Normal file
25
app/views/books/edit.html.erb
Normal file
@@ -0,0 +1,25 @@
|
||||
<% content_for(:title) { "Edit #{@book.title}" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to book_slug_path(@book), class: "btn flex-item-justify-start" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Cancel and go back</span>
|
||||
<% end %>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<%= render "books/form", book: @book, users: @users, creating_user: nil %>
|
||||
|
||||
<% content_for :footer do %>
|
||||
<nav class="flex align-end justify-center pad">
|
||||
<%= button_to book_path(@book), method: :delete, class: "btn btn--negative",
|
||||
data: { turbo_confirm: "Are you sure you want to delete this book? It cannot be undone." } do %>
|
||||
<%= image_tag "trash.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Delete <%= @book.title %></span>
|
||||
<% end %>
|
||||
<button type="submit" form="book-editor", class="new-book-btn btn btn--reversed center txt-medium--responsive" aria-label="Save changes" title="Save changes">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
</button>
|
||||
</nav>
|
||||
<% end %>
|
||||
32
app/views/books/index.html.erb
Normal file
32
app/views/books/index.html.erb
Normal file
@@ -0,0 +1,32 @@
|
||||
<% content_for(:title) { "Library | Writebook" } %>
|
||||
<% @layout_class = "books" %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<span class="btn btn--placeholder" aria-hidden="true"></span>
|
||||
|
||||
<a href="https://once.com/writebook" class="product__wordmark btn btn--plain txt-large center" target="_blank">
|
||||
<%= image_tag "writebook-icon.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span>Writebook</span>
|
||||
</a>
|
||||
|
||||
<% if Current.user %>
|
||||
<%= link_to users_path, class: "btn" do %>
|
||||
<%= image_tag "settings.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Manage people and settings</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to new_session_path, class: "btn" do %>
|
||||
<%= image_tag "login-keys.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Sign in</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<% cache [ @books, signed_in? ] do %>
|
||||
<div class="library">
|
||||
<%= render @books %>
|
||||
<%= render "books/new" if signed_in? %>
|
||||
</div>
|
||||
<% end %>
|
||||
20
app/views/books/new.html.erb
Normal file
20
app/views/books/new.html.erb
Normal file
@@ -0,0 +1,20 @@
|
||||
<% content_for(:title) { "Create a new book" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to root_path, class: "btn flex-item-justify-start" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Cancel and go back</span>
|
||||
<% end %>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<%= render "books/form", book: @book, users: @users, creating_user: Current.user %>
|
||||
|
||||
<% content_for :footer do %>
|
||||
<nav class="new-book-btn flex justify-center pad">
|
||||
<button type="submit" form="book-editor", class="btn btn--reversed txt-medium--responsive" aria-label="Create book" title="Create book">
|
||||
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
|
||||
</button>
|
||||
</nav>
|
||||
<% end %>
|
||||
63
app/views/books/publications/_publication.html.erb
Normal file
63
app/views/books/publications/_publication.html.erb
Normal file
@@ -0,0 +1,63 @@
|
||||
<div class="flex flex-column gap margin-block-start pad <%= book.published? ? "fill-selected" : "fill-shade" %> border-radius">
|
||||
<% if book.editable? %>
|
||||
<div class="flex align-center justify-center gap-half center">
|
||||
<%= image_tag "lock.svg", aria: { hidden: true }, size: 36, class: "colorize--black" %>
|
||||
<%= form_with model: book, url: book_publication_path(book), data: { controller: "form", action: "change->form#submit" }, html: { contents: true } do |form| %>
|
||||
<label class="switch txt-medium">
|
||||
<%= form.check_box :published, checked: book.published?, class: "switch__input" %>
|
||||
<span class="switch__btn round"></span>
|
||||
<span class="for-screen-reader">Publish this book</span>
|
||||
</label>
|
||||
<% end %>
|
||||
<%= image_tag "world.svg", aria: { hidden: true }, size: 36, class: "colorize--black" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if book.published? %>
|
||||
<div class="flex flex-column align-center gap txt-medium">
|
||||
<% public_url = book_slug_url(book) %>
|
||||
|
||||
<label class="flex flex-column gap full-width txt-align-center">
|
||||
<strong id="invite_label" class="invite-label for-screen-reader">Public link to this book</strong>
|
||||
<input type="text" class="input fill-white" id="invite_url" value="<%= public_url %>" aria-labelledby="invite_label" readonly>
|
||||
</label>
|
||||
|
||||
<div class="flex align-center gap">
|
||||
<div data-controller="dialog" class="flex-inline">
|
||||
<%= tag.button class: "btn", data: { action: "dialog#open" } do %>
|
||||
<%= image_tag "qr-code.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Show public link QR code</span>
|
||||
<% end %>
|
||||
|
||||
<dialog class="dialog panel shadow" data-dialog-target="menu">
|
||||
<%= qr_code_image(public_url) %>
|
||||
|
||||
<form method="dialog" class="margin-block-start flex justify-center">
|
||||
<button class="btn">
|
||||
<%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Close</span>
|
||||
</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
<%= button_to_copy_to_clipboard(public_url) do %>
|
||||
<%= image_tag "copy-paste.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Copy public link</span>
|
||||
<% end %>
|
||||
|
||||
<%= web_share_button(public_url, "Link to join Writebook", "Hit this link to join me in Writebook and start writing.") do %>
|
||||
<%= image_tag "share.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Share public link</span>
|
||||
<% end %>
|
||||
|
||||
<% if book.editable? %>
|
||||
<%= link_to edit_book_publication_path(book), class: "btn" do %>
|
||||
<%= image_tag "pencil.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Edit link URL</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
38
app/views/books/publications/edit.html.erb
Normal file
38
app/views/books/publications/edit.html.erb
Normal file
@@ -0,0 +1,38 @@
|
||||
<%= turbo_frame_tag @book, :publication do %>
|
||||
<div class="flex flex-column gap margin-block-start pad fill-shade border-radius <%= "shake" if @book.errors[:slug].any? %>">
|
||||
<div class="flex flex-column align-center gap txt-medium">
|
||||
<% public_url = book_url(@book) %>
|
||||
|
||||
<%= form_with model: @book, url: book_publication_path(@book), class: "max-width", data: { turbo_frame: "_top" } do |form| %>
|
||||
<%= form.hidden_field :publication, value: true %>
|
||||
|
||||
<label class="flex flex-column gap full-width txt-align-center">
|
||||
<div id="public_link_label" class="flex align-center gap justify-center">
|
||||
<strong>Edit publication link</strong>
|
||||
</div>
|
||||
<span class="input input--actor flex align-center fill-white">
|
||||
<%= "#{request.host}/#{@book.id}/" %>
|
||||
<%= form.text_field :slug,
|
||||
autofocus: true,
|
||||
class: class_names("input"),
|
||||
required: true,
|
||||
pattern: "^[\\-A-Za-z0-9]+$",
|
||||
title: "Enter letters, numbers, or hyphens only" %>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="flex align-center gap margin-inline margin-block-start">
|
||||
<button type="submit" class="btn btn--positive flex-item-justify-end">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
|
||||
<%= link_to book_slug_path(@book), class: "btn flex-item-justify-start" do %>
|
||||
<%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Cancel</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
3
app/views/books/publications/show.html.erb
Normal file
3
app/views/books/publications/show.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= turbo_frame_tag @book, :publication do %>
|
||||
<%= render "publication", book: @book %>
|
||||
<% end %>
|
||||
131
app/views/books/show.html.erb
Normal file
131
app/views/books/show.html.erb
Normal file
@@ -0,0 +1,131 @@
|
||||
<% content_for(:title) { @book.title } %>
|
||||
<% @layout_class = "book" %>
|
||||
|
||||
<% content_for :head do %>
|
||||
<%= tag.meta property: "og:title", content: @book.title %>
|
||||
<%= tag.meta property: "og:description", content: @book.subtitle %>
|
||||
<%= tag.meta property: "og:image", content: @book.cover.blank? ? asset_url("covers/cover-#{@book.theme}-og.png") : "#{root_url}#{url_for(@book.cover)}" %>
|
||||
<%= tag.meta property: "og:url", content: book_slug_url(@book) %>
|
||||
|
||||
<%= tag.meta property: "twitter:title", content: @book.title %>
|
||||
<%= tag.meta property: "twitter:description", content: @book.subtitle %>
|
||||
<%= tag.meta property: "twitter:image", content: @book.cover.blank? ? asset_url("covers/cover-#{@book.theme}-og.png") : "#{root_url}#{url_for(@book.cover)}" %>
|
||||
<%= tag.meta property: "twitter:card", content: "summary_large_image" %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav class="book__navbar">
|
||||
<%= link_to root_path, class: "btn" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Go back</span>
|
||||
<% end %>
|
||||
|
||||
<span class="btn btn--placeholder placeholder-start" aria-hidden="true"></span>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<strong><%= @book.title %></strong>
|
||||
</div>
|
||||
|
||||
<%= link_to_first_leafable(@leaves) %>
|
||||
|
||||
<span class="btn btn--placeholder placeholder-end" aria-hidden="true"></span>
|
||||
|
||||
<button class="btn fullscreen" data-action="fullscreen#toggle" data-fullscreen-target="button">
|
||||
<%= image_tag "expand.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Enter fullscreen</span>
|
||||
</button>
|
||||
|
||||
<% if @book.editable? %>
|
||||
<%= link_to edit_book_path(@book), class: "btn settings" do %>
|
||||
<%= image_tag "settings.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Edit book settings</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<% cache [ @book, @book.editable? ] do %>
|
||||
<aside class="txt-align-center margin-block">
|
||||
<div class="book__sidebar <%= "theme--#{@book&.theme}" unless @book.cover.attached? %>">
|
||||
<% if @book.cover.attached? %>
|
||||
<%= link_to rails_blob_path(@book.cover, disposition: "attachment", only_path: true), data: { action: "lightbox#open:prevent", lightbox_target: "image", lightbox_url_value: rails_blob_path(@book.cover, disposition: "attachment", only_path: true) } do %>
|
||||
<%= image_tag @book.cover, alt: "Cover for #{ @book.title }", class: "book__cover margin-block-none center" %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<span class="book__cover-wrapper">
|
||||
<%= image_tag "empty-cover.png", alt: "Book cover", class: "book__cover margin-block-none center" %>
|
||||
<span class="book__title overflow-line-clamp pad txt-align-start txt-tight-lines"" style="--lines: 6" aria-hidden="true"><%= @book.title %></span>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<% if @book.editable? %>
|
||||
<%= turbo_frame_tag @book, :publication do %>
|
||||
<%= render "books/publications/publication", book: @book %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<span data-controller="edit-mode" data-edit-mode-editing-class="edit-mode" />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<%= arrangement_tag @book, class: "arrangement__container toc__container full-width txt-align-center" do %>
|
||||
<h1 class="flex flex-column txt-tight-lines txt-align-start margin-block-end">
|
||||
<strong class="book__title txt-x-large--responsive"><%= @book.title %></strong>
|
||||
<span class="txt-large--responsive txt-normal"><%= @book.subtitle %></span>
|
||||
<span class="txt-large--responsive txt-normal"><%= @book.author %></span>
|
||||
</h1>
|
||||
|
||||
<div class="book__toolbar fill-white flex gap-half pad-block margin-block-end-half justify-center <%= "position-sticky" if @book.editable? %>" data-controller="toc-view" data-toc-view-id-value="<%= dom_id(@book) %>">
|
||||
<label class="btn txt-medium disable-when-empty">
|
||||
<input type="radio" name="view" id="toc-list" value="list" data-toc-view-target="switch" data-toc-view-type-value="list" data-action="toc-view#saveViewPref">
|
||||
<%= image_tag "view-list.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">List view</span>
|
||||
</label>
|
||||
|
||||
<label class="btn txt-medium flex-item-justify-start disable-when-empty">
|
||||
<input type="radio" name="view" id="toc-grid" value="grid" checked="checked" data-toc-view-target="switch" data-toc-view-type-value="grid" data-action="toc-view#saveViewPref">
|
||||
<%= image_tag "view-grid.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Page view</span>
|
||||
</label>
|
||||
|
||||
<% if @book.editable? %>
|
||||
<%= render "books/create_buttons", book: @book %>
|
||||
<%= render "books/mode_buttons", book: @book %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="position-relative">
|
||||
<% if @book.editable? %>
|
||||
<div class="toc__blank-slate align-center justify-start">
|
||||
<%= image_tag "blank-slate-arrows.svg", aria: { hidden: true }, size: 60, class: "colorize--black" %>
|
||||
<span class="flex align-center gap-half"><span>🇺🇸</span> <span>Pick a page type to get started</span></span>
|
||||
<span class="flex align-center gap-half"><span>🇪🇸</span> <span>Elige un tipo de página para comenzar</span></span>
|
||||
<span class="flex align-center gap-half"><span>🇫🇷</span> <span>Choisissez un type de page pour commencer</span></span>
|
||||
<span class="flex align-center gap-half"><span>🇮🇳</span> <span>शुरू करने के लिए एक पृष्ठ प्रकार चुनें</span></span>
|
||||
<span class="flex align-center gap-half"><span>🇩🇪</span> <span>Wählen Sie einen Seitentyp, um zu beginnen</span></span>
|
||||
<span class="flex align-center gap-half"><span>🇧🇷</span> <span>Escolha um tipo de página para começar</span></span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<menu class="toc margin-none" tabindex="0" data-arrangement-target="container" data-action="<%= arrangement_actions %>">
|
||||
<%= turbo_frame_tag :leaves, data: { arrangement_target: "list" } do -%>
|
||||
<%= render partial: "leaves/leaf", collection: @leaves, as: :leaf -%>
|
||||
<% end -%>
|
||||
</menu>
|
||||
|
||||
<div data-arrangement-target="layer" class="toc"></div>
|
||||
<div data-arrangement-target="dragImage" class="arrangement-drag-image"></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :footer do %>
|
||||
<nav class="book__nav flex align-center justify-center">
|
||||
<a href="https://once.com/writebook" class="product__wordmark btn btn--plain txt-medium" target="_blank">
|
||||
<%= image_tag "writebook-icon.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span>Made with Writebook</span>
|
||||
</a>
|
||||
</nav>
|
||||
<% end %>
|
||||
1
app/views/books/users/accesses/create.html.erb
Normal file
1
app/views/books/users/accesses/create.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= render "books/accesses/access", user: @user, book: @book %>
|
||||
1
app/views/books/users/accesses/destroy.html.erb
Normal file
1
app/views/books/users/accesses/destroy.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= render "books/accesses/access", user: @user, book: @book %>
|
||||
36
app/views/first_runs/show.html.erb
Normal file
36
app/views/first_runs/show.html.erb
Normal file
@@ -0,0 +1,36 @@
|
||||
<% content_for(:title) { "Set up Writebook" } %>
|
||||
|
||||
<div class="panel shadow center margin-block-double <%="shake" if flash[:alert] %>">
|
||||
<%= image_tag "writebook-icon.svg", class: "product__logo center colorize--black", size: 130 %>
|
||||
<h1 class="txt-x- large margin-none-block-start margin-block-end-double">Writebook</h1>
|
||||
|
||||
<%= form_with model: @user, url: first_run_path, class: "flex flex-column gap" do |form| %>
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:user_name) %>
|
||||
<label class="flex align-center gap input input--actor txt-large">
|
||||
<%= form.text_field :name, class: "input", autocomplete: "name", placeholder: "Name", autofocus: true, required: true, data: { "1p-ignore": true } %>
|
||||
<%= image_tag "person.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:email_address) %>
|
||||
<label class="flex align-center gap input input--actor txt-large">
|
||||
<%= form.email_field :email_address, class: "input", autocomplete: "username", placeholder: "Email address", required: true %>
|
||||
<%= image_tag "email.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:password) %>
|
||||
<label class="flex align-center gap input input--actor txt-large">
|
||||
<%= form.password_field :password, class: "input", autocomplete: "new-password", placeholder: "Password", required: true, maxlength: 72 %>
|
||||
<%= image_tag "password.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" id="log_in" class="btn btn--reversed center">
|
||||
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Create your account</span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
10
app/views/layouts/_lightbox.html.erb
Normal file
10
app/views/layouts/_lightbox.html.erb
Normal file
@@ -0,0 +1,10 @@
|
||||
<dialog class="lightbox" aria-label="Image Viewer (Press escape to close)" data-lightbox-target="dialog" data-action="close->lightbox#reset">
|
||||
<img src="" class="lightbox__image" data-lightbox-target="zoomedImage" />
|
||||
|
||||
<form method="dialog" class="lightbox__btn">
|
||||
<button class="btn fill-white">
|
||||
<%= image_tag "remove.svg", aria: { hidden: "true" } %>
|
||||
<span class="for-screen-reader">Close image viewer</span>
|
||||
</button>
|
||||
</form>
|
||||
</dialog>
|
||||
53
app/views/layouts/application.html.erb
Normal file
53
app/views/layouts/application.html.erb
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><%= content_for(:title) || "Writebook" %></title>
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
|
||||
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, interactive-widget=resizes-content">
|
||||
<meta name="view-transition" content="same-origin">
|
||||
<meta name="turbo-cache-control" content="no-cache">
|
||||
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
<% if signed_in? %>
|
||||
<%= hide_from_user_style_tag %>
|
||||
<% end %>
|
||||
|
||||
<%= yield :head %>
|
||||
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="/favicon.png" type="image/png">
|
||||
<link rel="apple-touch-icon" href="/app-icon.png">
|
||||
|
||||
<%= stylesheet_link_tag :all, "data-turbo-track": "reload" %>
|
||||
<%= custom_styles_tag %>
|
||||
|
||||
<%= javascript_importmap_tags %>
|
||||
</head>
|
||||
|
||||
<body data-controller="fullscreen lightbox touch">
|
||||
<header id="header">
|
||||
<%= yield :header %>
|
||||
</header>
|
||||
|
||||
<div id="toolbar">
|
||||
<%= yield :toolbar %>
|
||||
</div>
|
||||
|
||||
<main id="main" class="<%= @layout_class %>">
|
||||
<%= yield %>
|
||||
</main>
|
||||
|
||||
<footer id="footer">
|
||||
<%= yield :footer %>
|
||||
</footer>
|
||||
|
||||
<%= render "layouts/lightbox" %>
|
||||
</body>
|
||||
</html>
|
||||
13
app/views/layouts/mailer.html.erb
Normal file
13
app/views/layouts/mailer.html.erb
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style>
|
||||
/* Email styles need to be inline */
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
1
app/views/layouts/mailer.text.erb
Normal file
1
app/views/layouts/mailer.text.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= yield %>
|
||||
18
app/views/leafables/_edit_header.html.erb
Normal file
18
app/views/leafables/_edit_header.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<nav>
|
||||
<%= link_to_previous_leafable(leaf, hotkey: false, for_edit: true) %>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= link_to book.title, book_slug_path(book) %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= form_with model: [ book, leaf ], url: leafable_path(leaf), class: "flex align-center gap-half", format: :html do |form| %>
|
||||
<strong class="min-width"><%= form.text_field :title, form: "leafable-editor", class: "input full-width", autocomplete: "off" %></strong>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<button class="btn" data-action="fullscreen#toggle" data-fullscreen-target="button">
|
||||
<%= image_tag "expand.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Enter fullscreen</span>
|
||||
</button>
|
||||
</nav>
|
||||
7
app/views/leafables/create.turbo_stream.erb
Normal file
7
app/views/leafables/create.turbo_stream.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<% if params[:position].present? %>
|
||||
<%= turbo_stream.replace :dragged_item, partial: "leaves/edit", locals: { leaf: @leaf } %>
|
||||
<% else %>
|
||||
<%= turbo_stream.append :leaves, partial: "leaves/edit", locals: { leaf: @leaf } %>
|
||||
<% end %>
|
||||
|
||||
<%= turbo_stream.scroll_into_view @leaf, animation: :wiggle %>
|
||||
1
app/views/leafables/destroy.turbo_stream.erb
Normal file
1
app/views/leafables/destroy.turbo_stream.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= turbo_stream.remove @leaf %>
|
||||
36
app/views/leafables/show.html.erb
Normal file
36
app/views/leafables/show.html.erb
Normal file
@@ -0,0 +1,36 @@
|
||||
<% content_for(:title) { page_title(@leaf, @book) } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<%= render "leaves/header", book: @book, leaf: @leaf %>
|
||||
<% end %>
|
||||
|
||||
<% if @leaf.section? %>
|
||||
<div class="page--section <%= "theme--dark" if @leaf.leafable.theme == "dark" %>">
|
||||
<h1><%= simple_format @leaf.section.body %></h1>
|
||||
</div>
|
||||
<% elsif @leaf.page? %>
|
||||
<div class="page--page">
|
||||
<%= sanitize_content @leaf.page.body.to_html %>
|
||||
</div>
|
||||
<% elsif @leaf.picture? %>
|
||||
<figure class="page--picture flex flex-column align-center gap margin-none">
|
||||
<% if @leaf.picture.image.attached? %>
|
||||
<%= link_to rails_blob_path(@leaf.picture.image), data: {
|
||||
action: "lightbox#open:prevent",
|
||||
lightbox_target: "image",
|
||||
lightbox_url_value: rails_blob_path(@leaf.picture.image, disposition: "attachment", only_path: true) } do %>
|
||||
<%= image_tag @leaf.picture.image.variant(:large), loading: "lazy" %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= image_tag "default-picture.webp", alt: "No image uploaded", loading: "lazy" %>
|
||||
<% end %>
|
||||
|
||||
<figcaption>
|
||||
<%= simple_format @leaf.picture.caption %>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<% end %>
|
||||
|
||||
<% content_for :footer do %>
|
||||
<%= render "leaves/navigation", leaf: @leaf %>
|
||||
<% end %>
|
||||
9
app/views/leaves/_being_edited_by.turbo_stream.erb
Normal file
9
app/views/leaves/_being_edited_by.turbo_stream.erb
Normal file
@@ -0,0 +1,9 @@
|
||||
<%= turbo_stream.append dom_id(leaf, :being_edited) do %>
|
||||
<%= tag.div id: dom_id(user, :being_edited_by),
|
||||
class: "flex-inline align-center justify-center margin-block-end-double txt-medium gap being-edited-by",
|
||||
data: { controller: "autoremove", action: "animationend->autoremove#remove", hide_from_user_id: user.id } do %>
|
||||
<strong class="margin-inline-end-half"><%= user.name %></strong>
|
||||
<span class="spinner txt-small flex-inline"></span>
|
||||
<%= image_tag "write.svg", aria: { hidden: true }, size: 18, class: "colorize--white margin-inline-start" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
2
app/views/leaves/_being_edited_indicator.html.erb
Normal file
2
app/views/leaves/_being_edited_indicator.html.erb
Normal file
@@ -0,0 +1,2 @@
|
||||
<%= turbo_stream_from leaf, :being_edited %>
|
||||
<%= tag.div id: dom_id(leaf, :being_edited), class: "being-edited-indicator" %>
|
||||
4
app/views/leaves/_delete.html.erb
Normal file
4
app/views/leaves/_delete.html.erb
Normal file
@@ -0,0 +1,4 @@
|
||||
<%= button_to leafable_path(leaf), method: :delete, data: { turbo_confirm: "Are you sure you want to delete this page?" }, class: "btn btn--negative txt-small min-width", form_class: "leaf__delete" do %>
|
||||
<%= image_tag "minus.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Delete <%= leaf.title %></span>
|
||||
<% end %>
|
||||
38
app/views/leaves/_edit.html.erb
Normal file
38
app/views/leaves/_edit.html.erb
Normal file
@@ -0,0 +1,38 @@
|
||||
<%= leaf_item_tag(leaf) do %>
|
||||
<span class="btn btn--link arrangement__handle txt-small">
|
||||
<%= image_tag "handle.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Move <%= leaf.title %></span>
|
||||
</span>
|
||||
|
||||
<%= link_to leafable_path(leaf), class: "toc__thumbnail", data: { turbo_frame: "_top" } do %>
|
||||
<%= leaf.section.body if leaf.section? %>
|
||||
<%= leaf.leafable.body.to_html if leaf.page? %>
|
||||
<%= image_tag leaf.leafable.image.variant(:large) if leaf.picture&.image&.attached? %>
|
||||
<% end %>
|
||||
|
||||
<div class="toc__title flex align-center min-width">
|
||||
<%= form_with model: [ leaf.book, leaf ], url: leafable_path(leaf), class: "flex align-center max-width min-width", id: dom_id(leaf, "form") do |form| %>
|
||||
<%= form.text_field :title, class: "input full-width", autofocus: true, autocomplete: "off", data: { controller: "autoselect" } %>
|
||||
<% end %>
|
||||
|
||||
<span class="flex align-center gap-half">
|
||||
<button type="submit" class="btn txt-small btn--positive" form="<%= dom_id(leaf, "form") %>">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
|
||||
<%= button_to leafable_path(leaf), method: :delete, class: "btn btn--negative txt-small" do %>
|
||||
<%= image_tag "trash.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Delete this page</span>
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<small class="toc__wordcount">
|
||||
<% if leaf.page? %>
|
||||
<%= word_count(leaf.leafable.body.content) %>
|
||||
<% end %>
|
||||
</small>
|
||||
|
||||
<%= yield if block_given? %>
|
||||
<% end %>
|
||||
16
app/views/leaves/_edit_footer.html.erb
Normal file
16
app/views/leaves/_edit_footer.html.erb
Normal file
@@ -0,0 +1,16 @@
|
||||
<% content_for :footer do %>
|
||||
<nav class="book__nav flex align-center gap">
|
||||
<span class="btn btn--placeholder flex-item-justify-start"></span>
|
||||
|
||||
<%= link_to_next_leafable(leaf, hotkey: false, for_edit: true) %>
|
||||
|
||||
<span class="flex-item-justify-end">
|
||||
<%= form_with url: leafable_path(leaf, format: :html), class: "flex align-center gap-half", method: :delete do |form| %>
|
||||
<%= form.button class: "btn btn--negative txt-small min-width", data: { turbo_confirm: "Are you sure you want to delete this page?" } do %>
|
||||
<%= image_tag "minus.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Delete <%= @leaf.title %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</span>
|
||||
</nav>
|
||||
<% end %>
|
||||
26
app/views/leaves/_header.html.erb
Normal file
26
app/views/leaves/_header.html.erb
Normal file
@@ -0,0 +1,26 @@
|
||||
<% content_for :header do %>
|
||||
<%= leaf_nav_tag(leaf) do %>
|
||||
<%= link_to_previous_leafable(leaf) %>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= link_to book.title, book_slug_path(book) %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<strong><%= leaf.leafable.title %></strong>
|
||||
</div>
|
||||
|
||||
<button class="btn" data-action="fullscreen#toggle" data-fullscreen-target="button">
|
||||
<%= image_tag "expand.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Enter fullscreen</span>
|
||||
</button>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :toolbar do %>
|
||||
<% if book.editable? %>
|
||||
<div class="page-toolbar fill-shade">
|
||||
<%= editing_mode_toggle_switch(@leaf, checked: false) %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
6
app/views/leaves/_history.html.erb
Normal file
6
app/views/leaves/_history.html.erb
Normal file
@@ -0,0 +1,6 @@
|
||||
<% if leaf.edits.any? %>
|
||||
<%= link_to page_edit_path(leaf, "latest"), class: "btn flex-item-no-shrink txt-small" do %>
|
||||
<%= image_tag "history.svg", aria: { hidden: "true" }, size: "24" %>
|
||||
<span class="for-screen-reader">Editing history</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
60
app/views/leaves/_leaf.html.erb
Normal file
60
app/views/leaves/_leaf.html.erb
Normal file
@@ -0,0 +1,60 @@
|
||||
<% cache [ leaf, leaf.book, leaf.book.editable? ] do %>
|
||||
<%= leaf_item_tag(leaf) do %>
|
||||
<span class="btn btn--link arrangement__handle txt-small">
|
||||
<%= image_tag "handle.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Move <%= leaf.title %></span>
|
||||
</span>
|
||||
|
||||
<%= render "leaves/delete", leaf: leaf %>
|
||||
|
||||
<div class="toc__thumbnail <%= "toc__thumbnail--dark" if leaf.section&.theme == "dark" %>">
|
||||
<% if leaf.book.editable? %>
|
||||
<%= link_to edit_leafable_path(leaf), class: "toc__link hide_from_reading_mode", data: { turbo_frame: "_top" } do %>
|
||||
<span class="for-screen-reader">Edit <%= leaf.title %></span>
|
||||
<% end %>
|
||||
|
||||
<%= link_to leafable_slug_path(leaf), class: "toc__link hide_from_edit_mode", data: { turbo_frame: "_top" } do %>
|
||||
<span class="for-screen-reader">Open <%= leaf.title %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to leafable_slug_path(leaf), class: "toc__link", data: { turbo_frame: "_top" } do %>
|
||||
<span class="for-screen-reader">Open <%= leaf.title %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= tag.span(simple_format(leaf.section.body), class: "txt-align-center") if leaf.section? %>
|
||||
|
||||
<%= sanitize_content leaf.page.html_preview if leaf.page? %>
|
||||
|
||||
<% if leaf.picture? %>
|
||||
<%= image_tag leaf.leafable.image.attached? ? leaf.leafable.image.variant(:large) : "default-picture.webp" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if leaf.book.editable? %>
|
||||
<%= link_to edit_leafable_path(leaf), class: "toc__title min-width hide_from_reading_mode", data: { turbo_frame: "_top" } do %>
|
||||
<span class="overflow-ellipsis"><%= leaf.title %></span>
|
||||
<% end %>
|
||||
|
||||
<%= link_to leafable_slug_path(leaf), class: "toc__title min-width hide_from_edit_mode", data: { turbo_frame: "_top" } do %>
|
||||
<span class="overflow-ellipsis"><%= leaf.title %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to leafable_slug_path(leaf), class: "toc__title min-width", data: { turbo_frame: "_top" } do %>
|
||||
<span class="overflow-ellipsis"><%= leaf.title %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<span class="toc__bookmark">
|
||||
<svg viewBox="0 0 64 64" width="64px" height="64px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m17.01 54.01v-42.98.00000045c-.00000025-1.65685 1.34315-3 3-3h24-.00000013c1.65685-.00000007 3 1.34315 3 3v42.98.0001206c0 1.10457-.895431 2-2 2-.34994 0-.693756-.0918173-.997105-.266281l-11.5039-6.61564c-.308638-.17748-.688362-.17748-.997 0l-11.5046 6.6153.00000011-.00000006c-.957413.550849-2.1801.221264-2.73095-.736149-.174564-.303405-.266442-.647312-.266451-.997351z" fill-rule="evenodd" fill="var(--color-marker)" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<small class="toc__wordcount txt-small--responsive">
|
||||
<% if leaf.page? %>
|
||||
<%= word_count(leaf.leafable.body.content) %>
|
||||
<% end %>
|
||||
</small>
|
||||
<% end %>
|
||||
<% end %>
|
||||
3
app/views/leaves/_navigation.html.erb
Normal file
3
app/views/leaves/_navigation.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<nav class="book__nav flex align-center gap justify-center">
|
||||
<%= link_to_next_leafable(leaf) %>
|
||||
</nav>
|
||||
3
app/views/pages/_form.html.erb
Normal file
3
app/views/pages/_form.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= leafable_edit_form(page, id: "leafable-editor") do |form| %>
|
||||
<%= form.markdown_area :body, toolbar: "house_toolbar", required: true, autofocus: true, class: "page__editor" %>
|
||||
<% end %>
|
||||
41
app/views/pages/_house_toolbar.html.erb
Normal file
41
app/views/pages/_house_toolbar.html.erb
Normal file
@@ -0,0 +1,41 @@
|
||||
<%= house_toolbar id: "house_toolbar" do %>
|
||||
<%= house_toolbar_button "bold" do %>
|
||||
<%= image_tag "text-bold.svg", aria: { hidden: "true" }, size: "16", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Text style: bold</span>
|
||||
<% end %>
|
||||
|
||||
<%= house_toolbar_button "italic" do %>
|
||||
<%= image_tag "text-italic.svg", aria: { hidden: "true" }, size: "16", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Text style: italic</span>
|
||||
<% end %>
|
||||
|
||||
<%= house_toolbar_button "quote" do %>
|
||||
<%= image_tag "text-quote.svg", aria: { hidden: "true" }, size: "18", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Text style: quote</span>
|
||||
<% end %>
|
||||
|
||||
<%= house_toolbar_button "code" do %>
|
||||
<%= image_tag "text-code.svg", aria: { hidden: "true" }, size: "18", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Text style: code</span>
|
||||
<% end %>
|
||||
|
||||
<%= house_toolbar_button "link" do %>
|
||||
<%= image_tag "text-link.svg", aria: { hidden: "true" }, size: "18", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Text style: link</span>
|
||||
<% end %>
|
||||
|
||||
<%= house_toolbar_button "bulletList" do %>
|
||||
<%= image_tag "text-bullets.svg", aria: { hidden: "true" }, size: "18", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Text style: bulleted list</span>
|
||||
<% end %>
|
||||
|
||||
<%= house_toolbar_button "numberList" do %>
|
||||
<%= image_tag "text-numbers.svg", aria: { hidden: "true" }, size: "18", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Text style: numbered list</span>
|
||||
<% end %>
|
||||
|
||||
<%= house_toolbar_file_upload_button do %>
|
||||
<%= image_tag "text-image.svg", aria: { hidden: "true" }, size: "18", class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Add image</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
34
app/views/pages/edit.html.erb
Normal file
34
app/views/pages/edit.html.erb
Normal file
@@ -0,0 +1,34 @@
|
||||
<% content_for(:title) { "Edit #{ @page.title }" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<%= render "leafables/edit_header", leaf: @leaf, book: @book %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :toolbar do %>
|
||||
<div class="page-toolbar fill-selected align-center gap-half margin-block-end-double">
|
||||
<%= editing_mode_toggle_switch(@leaf, checked: true) %>
|
||||
|
||||
<span class="separator margin-inline-half" aria-hidden="true"></span>
|
||||
|
||||
<span class="overflow-y overflow-hide-scrollbar">
|
||||
<%= render "house_toolbar" %>
|
||||
</span>
|
||||
|
||||
<span class="separator margin-inline-half" aria-hidden="true"></span>
|
||||
|
||||
<%= render "leaves/history", leaf: @leaf %>
|
||||
|
||||
<button type="submit" form="leafable-editor" class="btn flex page-toolbar__save flex-item-justify-end flex-item-no-shrink txt-small">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<article class="layout--reading">
|
||||
<%= render "leaves/being_edited_indicator", leaf: @leaf %>
|
||||
|
||||
<%= render "pages/form", book: @book, page: @page %>
|
||||
</article>
|
||||
|
||||
<%= render "leaves/edit_footer", leaf: @leaf %>
|
||||
67
app/views/pages/edits/show.html.erb
Normal file
67
app/views/pages/edits/show.html.erb
Normal file
@@ -0,0 +1,67 @@
|
||||
<% content_for(:title) { "Changes to #{ @leaf.title }" } %>
|
||||
<% @layout_class = "books" %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to edit_leafable_path(@leaf), class: "btn" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Go back</span>
|
||||
<% end %>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= link_to @leaf.book.title, @leaf.book %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<strong><%= @leaf.title %></strong>
|
||||
</div>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="library">
|
||||
<section class="txt-align-start">
|
||||
<%= turbo_frame_tag :previous_version do %>
|
||||
<header class="flex align-center justify-center gap">
|
||||
<% if @edit.previous %>
|
||||
<%= link_to page_edit_path(@leaf, @edit.previous), data: { turbo_action: :advance }, class: "btn btn--reversed txt-small" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Previous version</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<span class="btn btn-reversed txt-small" disabled>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Previous version</span>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<h2 class="btn btn--reversed txt-medium margin-none overflow-ellipsis">
|
||||
<%= time_ago_in_words(@edit.updated_at) %> ago
|
||||
</h2>
|
||||
|
||||
<% if @edit.next %>
|
||||
<%= link_to page_edit_path(@leaf, @edit.next), data: { turbo_action: :advance }, class: "btn btn--reversed txt-small" do %>
|
||||
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Next version</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<span class="btn btn-reversed txt-small" disabled>
|
||||
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Next version</span>
|
||||
</span>
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<%= @edit.page.body.to_html %>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
<section class="page-edit__current txt-align-start">
|
||||
<header class="flex align-center gap justify-center">
|
||||
<%= link_to edit_leafable_path(@leaf), class: "btn btn--positive txt-medium center margin-none" do %>
|
||||
Current
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<%= @leaf.page.body.to_html %>
|
||||
</section>
|
||||
</div>
|
||||
19
app/views/pages/new.html.erb
Normal file
19
app/views/pages/new.html.erb
Normal file
@@ -0,0 +1,19 @@
|
||||
<% content_for(:title) { "New page" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to @book, class: "btn flex-item-justify-start" do %>
|
||||
<%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Cancel</span>
|
||||
<% end %>
|
||||
|
||||
<button type="submit" form="leafable-editor", class="btn flex-item-justify-end">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<article class="layout--reading">
|
||||
<%= render "pages/form", book: @book, page: @page %>
|
||||
</article>
|
||||
3
app/views/pages/update.turbo_stream.erb
Normal file
3
app/views/pages/update.turbo_stream.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= turbo_stream.replace dom_id(@leaf) do %>
|
||||
<%= render "leaves/leaf", leaf: @leaf %>
|
||||
<% end %>
|
||||
13
app/views/pictures/_form.html.erb
Normal file
13
app/views/pictures/_form.html.erb
Normal file
@@ -0,0 +1,13 @@
|
||||
<%= leafable_edit_form(picture, id: "leafable-editor") do |form| %>
|
||||
<label class="input input--file input--picture unpad" data-controller="upload-preview">
|
||||
<%= image_tag picture.image.attached? ? picture.image.variant(:large) : "default-picture.webp", alt: "Picture",
|
||||
data: { upload_preview_target: "image" } %>
|
||||
<%= form.file_field :image, class: "input", accept: "image/png, image/jpeg, image/jpg, image/webp", autofocus: true,
|
||||
data: { upload_preview_target: "input", action: "upload-preview#previewImage" } %>
|
||||
</label>
|
||||
<div class="flex align-center gap margin-block">
|
||||
<%= translation_button(:picture_caption) %>
|
||||
<%= form.text_field :caption, class: "input", placeholder: "Picture caption" %>
|
||||
</div>
|
||||
<%= form.submit "Save", hidden: true, data: { upload_preview_target: "button" } %>
|
||||
<% end %>
|
||||
24
app/views/pictures/edit.html.erb
Normal file
24
app/views/pictures/edit.html.erb
Normal file
@@ -0,0 +1,24 @@
|
||||
<% content_for(:title) { "Edit #{ @picture.title }" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<%= render "leafables/edit_header", leaf: @leaf, book: @book %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :toolbar do %>
|
||||
<div class="page-toolbar fill-selected align-center gap-half margin-block-end-double">
|
||||
<%= editing_mode_toggle_switch(@leaf, checked: true) %>
|
||||
|
||||
<span class="separator margin-inline-half" aria-hidden="true"></span>
|
||||
|
||||
<button type="submit" form="leafable-editor" class="btn flex page-toolbar__save flex-item-justify-end flex-item-no-shrink txt-small">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="page--picture picture-form margin-none">
|
||||
<%= render "pictures/form", book: @book, picture: @picture %>
|
||||
</div>
|
||||
|
||||
<%= render "leaves/edit_footer", leaf: @leaf %>
|
||||
19
app/views/pictures/new.html.erb
Normal file
19
app/views/pictures/new.html.erb
Normal file
@@ -0,0 +1,19 @@
|
||||
<% content_for(:title) { "New image page" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to @book, class: "btn flex-item-justify-start" do %>
|
||||
<%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Cancel</span>
|
||||
<% end %>
|
||||
|
||||
<button type="submit" form="leafable-editor", class="btn flex-item-justify-end">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="page--picture">
|
||||
<%= render "pictures/form", book: @book, picture: @picture %>
|
||||
</div>
|
||||
3
app/views/pictures/update.turbo_stream.erb
Normal file
3
app/views/pictures/update.turbo_stream.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= turbo_stream.replace dom_id(@leaf) do %>
|
||||
<%= render "leaves/leaf", leaf: @leaf %>
|
||||
<% end %>
|
||||
42
app/views/pwa/manifest.json.erb
Normal file
42
app/views/pwa/manifest.json.erb
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "Writebook",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/app-icon-192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "/app-icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
},
|
||||
{
|
||||
"src": "/app-icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"scope": "/",
|
||||
"description": "Writebook is remarkably simple software that allows you to publish text and pictures in a simple, browsable online book format.",
|
||||
"categories": ["books", "business", "productivity"],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Create a new book",
|
||||
"description": "Open Writebook and start writing",
|
||||
"url": "/books/new",
|
||||
"icons": [{ "src": "<%= image_url("add.svg") %>", "sizes": "any" }]
|
||||
},
|
||||
{
|
||||
"name": "Open library",
|
||||
"description": "Open Writebook and see all your books",
|
||||
"url": "/books",
|
||||
"icons": [{ "src": "<%= image_url("books.svg") %>", "sizes": "any" }]
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
26
app/views/pwa/service-worker.js
Normal file
26
app/views/pwa/service-worker.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// Add a service worker for processing Web Push notifications:
|
||||
//
|
||||
// self.addEventListener("push", async (event) => {
|
||||
// const { title, options } = await event.data.json()
|
||||
// event.waitUntil(self.registration.showNotification(title, options))
|
||||
// })
|
||||
//
|
||||
// self.addEventListener("notificationclick", function(event) {
|
||||
// event.notification.close()
|
||||
// event.waitUntil(
|
||||
// clients.matchAll({ type: "window" }).then((clientList) => {
|
||||
// for (let i = 0; i < clientList.length; i++) {
|
||||
// let client = clientList[i]
|
||||
// let clientPath = (new URL(client.url)).pathname
|
||||
//
|
||||
// if (clientPath == event.notification.data.path && "focus" in client) {
|
||||
// return client.focus()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (clients.openWindow) {
|
||||
// return clients.openWindow(event.notification.data.path)
|
||||
// }
|
||||
// })
|
||||
// )
|
||||
// })
|
||||
4
app/views/sections/_form.html.erb
Normal file
4
app/views/sections/_form.html.erb
Normal file
@@ -0,0 +1,4 @@
|
||||
<%= leafable_edit_form(section, id: "leafable-editor") do |form| %>
|
||||
<h1><%= form.text_area :body, class: "input input--textara full-width txt-align-center", required: true, autocomplete: "off", autofocus: true %></h1>
|
||||
<%= form.submit "Save", hidden: true %>
|
||||
<% end %>
|
||||
33
app/views/sections/edit.html.erb
Normal file
33
app/views/sections/edit.html.erb
Normal file
@@ -0,0 +1,33 @@
|
||||
<% content_for(:title) { "Edit #{ @section.title }" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<%= render "leafables/edit_header", leaf: @leaf, book: @book %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :toolbar do %>
|
||||
<div class="page-toolbar fill-selected align-center gap-half margin-block-end-double">
|
||||
<%= editing_mode_toggle_switch(@leaf, checked: true) %>
|
||||
|
||||
<span class="separator margin-inline-half" aria-hidden="true"></span>
|
||||
|
||||
<label class="btn txt-small">
|
||||
<%= hidden_field_tag "section[theme]", nil, id: nil, form: "leafable-editor" %>
|
||||
<%= check_box_tag "section[theme]", "dark", @section.theme.present?, class: "switch__input", form: "leafable-editor" %>
|
||||
<%= image_tag "theme-switch.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Change theme</span>
|
||||
</label>
|
||||
|
||||
<span class="separator margin-inline-half" aria-hidden="true"></span>
|
||||
|
||||
<button type="submit" form="leafable-editor" class="btn flex page-toolbar__save flex-item-justify-end flex-item-no-shrink txt-small">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="page--section <%= "theme--dark" if @section.theme == "dark" %>">
|
||||
<%= render "sections/form", book: @book, section: @section %>
|
||||
</div>
|
||||
|
||||
<%= render "leaves/edit_footer", leaf: @leaf %>
|
||||
19
app/views/sections/new.html.erb
Normal file
19
app/views/sections/new.html.erb
Normal file
@@ -0,0 +1,19 @@
|
||||
<% content_for(:title) { "New section page" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to @book, class: "btn flex-item-justify-start" do %>
|
||||
<%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Cancel</span>
|
||||
<% end %>
|
||||
|
||||
<button type="submit" form="leafable-editor", class="btn flex-item-justify-end">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save</span>
|
||||
</button>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="page--section">
|
||||
<%= render "sections/form", book: @book, section: @section %>
|
||||
</div>
|
||||
3
app/views/sections/update.turbo_stream.erb
Normal file
3
app/views/sections/update.turbo_stream.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<%= turbo_stream.replace dom_id(@leaf) do %>
|
||||
<%= render "leaves/leaf", leaf: @leaf %>
|
||||
<% end %>
|
||||
33
app/views/sessions/new.html.erb
Normal file
33
app/views/sessions/new.html.erb
Normal file
@@ -0,0 +1,33 @@
|
||||
<% content_for(:title) { "Sign in" } %>
|
||||
<% turbo_page_requires_reload %>
|
||||
|
||||
<div class="panel shadow center margin-block-double <%= "shake" if flash[:alert] %>">
|
||||
<%= image_tag "writebook-icon.svg", class: "product__logo center colorize--black", size: 130 %>
|
||||
<h1 class="txt-x- large margin-none-block-start margin-block-end-double">Writebook</h1>
|
||||
|
||||
<%= form_with url: session_url, class: "flex flex-column gap" do |form| %>
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:email_address) %>
|
||||
<label class="flex align-center gap input input--actor txt-large">
|
||||
<%= form.email_field :email_address, required: true, class: "input full-width", autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address] %>
|
||||
<%= image_tag "email.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:password) %>
|
||||
<label class="flex align-center gap input input--actor txt-large">
|
||||
<%= form.password_field :password, required: true, class: "input full-width", autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72 %>
|
||||
<%= image_tag "password.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" id="log_in" class="btn btn--reversed center">
|
||||
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Sign in</span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% content_for(:footer) do %>
|
||||
<div class="txt-align-center center margin-block-double txt-subtle">Writebook™ version <%= version_badge %></div>
|
||||
<% end %>
|
||||
1
app/views/sessions/transfers/show.html.erb
Normal file
1
app/views/sessions/transfers/show.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= auto_submit_form_with method: :put %>
|
||||
47
app/views/users/_invite.html.erb
Normal file
47
app/views/users/_invite.html.erb
Normal file
@@ -0,0 +1,47 @@
|
||||
<div class="flex flex-column align-center gap txt-medium--responsive">
|
||||
<% url = join_url(Current.account.join_code) %>
|
||||
|
||||
<label class="flex flex-column gap full-width txt-align-center">
|
||||
<strong id="invite_label" class="invite-label">Share to invite more people</strong>
|
||||
<span class="flex align-center gap margin-inline">
|
||||
<input type="text" class="input fill-white" id="invite_url" value="<%= url %>" aria-labelledby="invite_label" readonly>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="flex align-center gap">
|
||||
<div data-controller="dialog" class="flex-inline">
|
||||
<%= tag.button class: "btn", data: { action: "dialog#open" } do %>
|
||||
<%= image_tag "qr-code.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Show join link QR code</span>
|
||||
<% end %>
|
||||
|
||||
<dialog class="dialog panel shadow" data-dialog-target="menu"">
|
||||
<%= qr_code_image(url) %>
|
||||
|
||||
<form method="dialog" class="margin-block-start flex justify-center">
|
||||
<button class="btn">
|
||||
<%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Close</span>
|
||||
</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
<%= button_to_copy_to_clipboard(url) do %>
|
||||
<%= image_tag "copy-paste.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Copy join link</span>
|
||||
<% end %>
|
||||
|
||||
<%= web_share_button(url, "Link to join Writebook", "Hit this link to join me in Writebook and start writing.") do %>
|
||||
<%= image_tag "share.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Share join link</span>
|
||||
<% end %>
|
||||
|
||||
<% if Current.user.can_administer? %>
|
||||
<%= button_to account_join_code_path, class: "btn btn--regenerate" do %>
|
||||
<%= image_tag "refresh.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Regenerate join link</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
48
app/views/users/_transfer.html.erb
Normal file
48
app/views/users/_transfer.html.erb
Normal file
@@ -0,0 +1,48 @@
|
||||
<div class="flex flex-column align-center gap txt-medium--responsive">
|
||||
<% url = session_transfer_url(user.transfer_id) %>
|
||||
|
||||
<label class="flex flex-column gap full-width">
|
||||
<div class="flex align-center gap">
|
||||
<% if Current.user != user %>
|
||||
<%= translation_button(:transfer_session) %>
|
||||
<strong id="session_transfer_label" class="txt-align-start">Share to get them back into their account</strong>
|
||||
<% else %>
|
||||
<%= translation_button(:transfer_session_self) %>
|
||||
<strong id="session_transfer_label" class="txt-align-start">Link to automatically log in on another device</strong>
|
||||
<% end %>
|
||||
</div>
|
||||
<span class="flex align-center gap margin-inline">
|
||||
<input type="text" class="input fill-white" id="session_transfer_url" value="<%= url %>" aria-labelledby="session_transfer_label" readonly>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="flex align-center gap">
|
||||
<div data-controller="dialog" class="flex-inline">
|
||||
<%= tag.button class: "btn", data: { action: "dialog#open" } do %>
|
||||
<%= image_tag "qr-code.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Show auto-login QR code</span>
|
||||
<% end %>
|
||||
|
||||
<dialog class="dialog panel shadow" data-dialog-target="menu"">
|
||||
<%= qr_code_image(url) %>
|
||||
|
||||
<form method="dialog" class="margin-block-start flex justify-center">
|
||||
<button class="btn">
|
||||
<%= image_tag "remove.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Close</span>
|
||||
</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
<%= button_to_copy_to_clipboard(url) do %>
|
||||
<%= image_tag "copy-paste.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Copy auto-login link</span>
|
||||
<% end %>
|
||||
|
||||
<%= web_share_button(url, "Your sign-in link", "This is your own private sign-in URL, DO NOT SHARE IT. Use it to sign-in on another device or if you get locked out.") do %>
|
||||
<%= image_tag "share.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
|
||||
<span class="for-screen-reader">Share auto-login link</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
18
app/views/users/_user.html.erb
Normal file
18
app/views/users/_user.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="flex align-center gap-half pad-inline txt-medium--responsive">
|
||||
<strong class="overflow-ellipsis txt-medium--responsive"><%= link_to user.name, user.current? ? edit_user_profile_path(user) : user_profile_path(user) %></strong>
|
||||
<hr class="flex-item-grow margin-none" aria-hidden="true">
|
||||
|
||||
<%= form_with model: user, url: user_path(user), data: { controller: "form" }, method: :patch do | form | %>
|
||||
<label class="btn btn--small flex-item-no-shrink" for="<%= dom_id(user, :role) %>">
|
||||
<span class="for-screen-reader"><%= user.name %>'s role: <%= user.administrator? ? "Administrator" : "Member" %></span>
|
||||
<%= image_tag "crown.svg", size: 24, aria: { hidden: "true" } %>
|
||||
<%= form.check_box :role, { data: { action: "form#submit" }, hidden: true, id: dom_id(user, :role), disabled: ("disabled" if user.current? || !Current.user.can_administer?) }, "administrator", "member" %>
|
||||
</label>
|
||||
<% end %>
|
||||
|
||||
<%= button_to user_path(user), method: :delete, class: "btn btn--small btn--negative flex-item-no-shrink", disabled: ("disabled" if user.current? || !Current.user.can_administer?), data: {
|
||||
turbo_confirm: "Are you sure you want to permanently remove this person from the account? This can’t be undone."} do %>
|
||||
<%= image_tag "minus.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Remove <%= user.name %> from the account</span>
|
||||
<% end %>
|
||||
</div>
|
||||
35
app/views/users/index.html.erb
Normal file
35
app/views/users/index.html.erb
Normal file
@@ -0,0 +1,35 @@
|
||||
<% content_for(:title) { "People on the account" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to root_path, class: "btn" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Go back</span>
|
||||
<% end %>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= image_tag "settings.svg", aria: { label: "Settings" }, size: 19, class: "colorize--black", alt: "Settings" %>
|
||||
</div>
|
||||
|
||||
<% if Current.user.can_administer? %>
|
||||
<%= link_to edit_account_custom_styles_url, class: "btn" do %>
|
||||
<%= image_tag "art.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Custom styles</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="panel borderless center pad fill-shade flex flex-column margin-block">
|
||||
<%= render "invite" %>
|
||||
</div>
|
||||
|
||||
<div class="panel borderless center pad fill-none flex flex-column gap">
|
||||
<%= render @users %>
|
||||
</div>
|
||||
|
||||
<% content_for(:footer) do %>
|
||||
<div class="txt-align-center center margin-block-double txt-subtle">Writebook™ version <%= version_badge %></div>
|
||||
<% end %>
|
||||
44
app/views/users/new.html.erb
Normal file
44
app/views/users/new.html.erb
Normal file
@@ -0,0 +1,44 @@
|
||||
<% content_for(:title) { "Create your account" } %>
|
||||
<% turbo_page_requires_reload %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to new_session_path, class: "btn flex-item-justify-end" do %>
|
||||
<%= image_tag "login-keys.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Sign in instead</span>
|
||||
<% end %>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="panel shadow center <%= "shake" if flash[:alert] %>">
|
||||
<%= image_tag "writebook-icon.svg", class: "product__logo center colorize--black", size: 130 %>
|
||||
<h1 class="txt-x- large margin-none-block-start margin-block-end-double">Writebook</h1>
|
||||
|
||||
<%= form_with model: @user, url: join_path(params[:join_code]), class: "flex flex-column gap" do |form| %>
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:user_name) %>
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.text_field :name, class: "input full-width", autocomplete: "name", placeholder: "Name", autofocus: true, required: true, data: { "1p-ignore": true } %>
|
||||
<%= image_tag "person.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:email_address) %>
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.email_field :email_address, class: "input full-width", autocomplete: "username", placeholder: "Email address", required: true %>
|
||||
<%= image_tag "email.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:password) %>
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.password_field :password, class: "input full-width", autocomplete: "new-password", placeholder: "Password", required: true, maxlength: 72 %>
|
||||
<%= image_tag "password.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" id="log_in" class="btn btn--reversed center">
|
||||
<%= image_tag "arrow-right.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Sign in</span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
63
app/views/users/profiles/edit.html.erb
Normal file
63
app/views/users/profiles/edit.html.erb
Normal file
@@ -0,0 +1,63 @@
|
||||
<% content_for(:title) { "Your settings" } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to users_path, class: "btn" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Go back</span>
|
||||
<% end %>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= link_to users_path, class: "btn borderless txt-small flex-item-no-shrink" do %>
|
||||
<%= image_tag "people.svg", aria: { label: "People" }, size: 19, class: "colorize--black", alt: "People" %>
|
||||
<span class="for-screen-reader">Manage people</span>
|
||||
<% end %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= @user.name %>
|
||||
</div>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="panel margin-block-double shadow center">
|
||||
<%= form_with model: @user, url: user_profile_path(@user), class: "flex flex-column gap" do |form| %>
|
||||
<%= form.hidden_field :role %>
|
||||
<div class="flex align-center gap margin-block-start">
|
||||
<%= translation_button(:user_name) %>
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.text_field :name, class: "input full-width", autocomplete: "name", placeholder: "Name", autofocus: true, required: true, data: { "1p-ignore": true } %>
|
||||
<%= image_tag "person.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:email_address) %>
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.email_field :email_address, class: "input full-width", autocomplete: "username", placeholder: "Email address", required: true %>
|
||||
<%= image_tag "email.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex align-center gap">
|
||||
<%= translation_button(:update_password) %>
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.password_field :password, class: "input full-width", autocomplete: "new-password", placeholder: "Change password", maxlength: 72 %>
|
||||
<%= image_tag "password.svg", aria: { hidden: "true" }, size: 30, class: "colorize--black" %>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn--reversed center">
|
||||
<%= image_tag "check.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Save changes</span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="panel margin-block-double shadow center">
|
||||
<%= render "users/transfer", user: @user %>
|
||||
</div>
|
||||
|
||||
<div class="panel margin-block-double shadow center">
|
||||
<%= button_to session_path, method: :delete, class: "btn center" do %>
|
||||
<%= image_tag "logout.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Sign out</span>
|
||||
<% end %>
|
||||
</div>
|
||||
31
app/views/users/profiles/show.html.erb
Normal file
31
app/views/users/profiles/show.html.erb
Normal file
@@ -0,0 +1,31 @@
|
||||
<% content_for(:title) { @user.name } %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<nav>
|
||||
<%= link_to users_path, class: "btn" do %>
|
||||
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
|
||||
<span class="for-screen-reader">Go back</span>
|
||||
<% end %>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<%= render "books/index_link" %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= link_to users_path, class: "btn borderless txt-small flex-item-no-shrink" do %>
|
||||
<%= image_tag "people.svg", aria: { label: "People" }, size: 19, class: "colorize--black", alt: "People" %>
|
||||
<span class="for-screen-reader">Manage people</span>
|
||||
<% end %>
|
||||
<span class="flex-item-no-shrink">▸</span>
|
||||
<%= @user.name %>
|
||||
</div>
|
||||
</nav>
|
||||
<% end %>
|
||||
|
||||
<div class="panel margin-block-double shadow center">
|
||||
<h2 class="margin-none-block-end"><%= @user.name %></h2>
|
||||
<p class="margin-none-block-start"><%= mail_to @user.email_address %></p>
|
||||
|
||||
<% if Current.user.can_administer? %>
|
||||
<hr class="full-width margin-block-double" aria-hidden="true">
|
||||
<%= render "users/transfer", user: @user %>
|
||||
<% end %>
|
||||
</div>
|
||||
Reference in New Issue
Block a user