This commit is contained in:
2025-11-07 13:34:32 -08:00
commit 1e8c5a972b
436 changed files with 11000 additions and 0 deletions

View 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 %>

View 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 %>

View 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 %>

View 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 %>

View 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 %>

View 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>

View 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>

View 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>

View 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 %>

View 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 %>

View 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 %>

View 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 %>

View 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 %>

View 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 %>

View 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 %>

View 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>

View 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 %>

View File

@@ -0,0 +1,3 @@
<%= turbo_frame_tag @book, :publication do %>
<%= render "publication", book: @book %>
<% end %>

View 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 %>

View File

@@ -0,0 +1 @@
<%= render "books/accesses/access", user: @user, book: @book %>

View File

@@ -0,0 +1 @@
<%= render "books/accesses/access", user: @user, book: @book %>