diff --git a/app/assets/images/arrow-right.svg b/app/assets/images/arrow-right.svg new file mode 100644 index 000000000..796f0cb1e --- /dev/null +++ b/app/assets/images/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/email.svg b/app/assets/images/email.svg new file mode 100644 index 000000000..83ed9d9ff --- /dev/null +++ b/app/assets/images/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/globe.svg b/app/assets/images/globe.svg new file mode 100644 index 000000000..a72720c68 --- /dev/null +++ b/app/assets/images/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/logout.svg b/app/assets/images/logout.svg new file mode 100644 index 000000000..a50034a98 --- /dev/null +++ b/app/assets/images/logout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/password.svg b/app/assets/images/password.svg new file mode 100644 index 000000000..48540ee91 --- /dev/null +++ b/app/assets/images/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/stylesheets/animation.css b/app/assets/stylesheets/animation.css new file mode 100644 index 000000000..f6da1028c --- /dev/null +++ b/app/assets/stylesheets/animation.css @@ -0,0 +1,12 @@ +/* Animation classes */ +.shake { + animation: shake 400ms both; +} + +/* Keyframes */ +@keyframes shake { + 0% { transform: translateX(-2rem); } + 25% { transform: translateX(2rem); } + 50% { transform: translateX(-1rem); } + 75% { transform: translateX(1rem); } +} diff --git a/app/assets/stylesheets/layout.css b/app/assets/stylesheets/layout.css index 0e8190d66..eb5931a6d 100644 --- a/app/assets/stylesheets/layout.css +++ b/app/assets/stylesheets/layout.css @@ -32,17 +32,9 @@ body { :where(#footer) { max-inline-size: 100vw; - view-transition-name: footer; > nav { padding-inline: var(--inline-space); - view-transition-name: footer-nav; - } - - &:has(.new-book-btn) { - inset-block-end: 0; - pointer-events: none; - position: sticky; } } @@ -67,19 +59,7 @@ body { } } -:where(#toolbar) { - display: flex; - inset: 0 0 auto; - justify-content: center; - min-width: 0; - overflow: hidden; - padding-inline: var(--inline-space); - position: sticky; - view-transition-name: toolbar; - z-index: 1; -} - -:is(#header, #footer, #toolbar) { +:is(#header, #footer) { @media print { display: none; } diff --git a/app/assets/stylesheets/translations.css b/app/assets/stylesheets/translations.css new file mode 100644 index 000000000..7cb7e921e --- /dev/null +++ b/app/assets/stylesheets/translations.css @@ -0,0 +1,33 @@ +/* Language translations */ +.lanuage-list-menu { + --max-width: 40ch; + + background-color: var(--color-subtle); + border: 1px solid var(--color-subtle-dark); + border-radius: 0.5em; + inset: auto; + inline-size: max-content; + margin-inline: var(--inline-space); + max-inline-size: 40ch; + overflow: clip; + position: absolute; + z-index: 1; + + @media (max-width: 70ch) { + max-inline-size: calc(var(--max-width) - var(--inline-space)); + } + + .popover-orientation-top & { + inset-block-end: 100%; + } +} + +.language-list { + display: grid; + gap: var(--block-space-half) var(--inline-space); + grid-template-rows: min-content; + grid-template-columns: min-content 1fr; + justify-content: start; + margin: 0; + text-align: start; +} diff --git a/app/helpers/translations_helper.rb b/app/helpers/translations_helper.rb new file mode 100644 index 000000000..2794d3a41 --- /dev/null +++ b/app/helpers/translations_helper.rb @@ -0,0 +1,27 @@ +module TranslationsHelper + TRANSLATIONS = { + email_address: { "🇺🇸": "Enter your email address", "🇪🇸": "Introduce tu correo electrónico", "🇫🇷": "Entrez votre adresse courriel", "🇮🇳": "अपना ईमेल पता दर्ज करें", "🇩🇪": "Geben Sie Ihre E-Mail-Adresse ein", "🇧🇷": "Insira seu endereço de email" }, + password: { "🇺🇸": "Enter your password", "🇪🇸": "Introduce tu contraseña", "🇫🇷": "Saisissez votre mot de passe", "🇮🇳": "अपना पासवर्ड दर्ज करें", "🇩🇪": "Geben Sie Ihr Passwort ein", "🇧🇷": "Insira sua senha" } + } + + def translations_for(translation_key) + tag.dl(class: "language-list") do + TRANSLATIONS[translation_key].map do |language, translation| + concat tag.dt(language) + concat tag.dd(translation, class: "margin-none") + end + end + end + + def translation_button(translation_key) + tag.div(class: "position-relative", data: { controller: "popover", action: "keydown.esc->popover#close click@document->popover#closeOnClickOutside", popover_orientation_top_class: "popover-orientation-top" }) do + tag.button(type: "button", class: "btn", tabindex: -1, data: { action: "popover#toggle" }) do + concat image_tag("globe.svg", size: 20, role: "presentation", class: "color-icon") + concat tag.span("Translate", class: "for-screen-reader") + end + + tag.dialog(class: "lanuage-list-menu popover shadow", data: { popover_target: "menu" }) do + translations_for(translation_key) + end + end + end +end diff --git a/app/javascript/controllers/popover_controller.js b/app/javascript/controllers/popover_controller.js new file mode 100644 index 000000000..3c872f071 --- /dev/null +++ b/app/javascript/controllers/popover_controller.js @@ -0,0 +1,43 @@ +import { Controller } from "@hotwired/stimulus" + +const BOTTOM_THRESHOLD = 0 + +export default class extends Controller { + static targets = [ "menu" ] + static classes = [ "orientationTop" ] + + close() { + this.menuTarget.close() + this.#orient() + } + + open() { + this.menuTarget.show() + this.#orient() + } + + toggle() { + this.menuTarget.open ? this.close() : this.open() + } + + closeOnClickOutside({ target }) { + if (!this.element.contains(target)) this.close() + } + + #orient() { + this.element.classList.toggle(this.orientationTopClass, this.#distanceToBottom < BOTTOM_THRESHOLD) + this.menuTarget.style.setProperty("--max-width", this.#maxWidth + "px") + } + + get #distanceToBottom() { + return window.innerHeight - this.#boundingClientRect.bottom + } + + get #maxWidth() { + return window.innerWidth - this.#boundingClientRect.left + } + + get #boundingClientRect() { + return this.menuTarget.getBoundingClientRect() + } +} diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index 4974b7d12..d9cdd3d79 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -1,9 +1,33 @@ -<%= form_with model: User.new, url: session_path do |f| %> - <%= f.label :email_address %> - <%= f.email_field :email_address %> +<% content_for(:title) { "Sign in" } %> +<% turbo_page_requires_reload %> - <%= f.label :password %> - <%= f.password_field :password %> +
" style="--panel-size: 40ch;"> + <%= image_tag "splat-1.svg", class: "product__logo center colorize--black", size: 130 %> +

Splat

- <%= f.submit "Sign in" %> + <%= form_with model: User.new, url: session_path, class: "flex flex-column gap" do |form| %> +
+ <%= translation_button(:email_address) %> + +
+ +
+ <%= translation_button(:password) %> + +
+ + <% end %> +
+ +<% content_for(:footer) do %> +
Splat™ version 0
<% end %> diff --git a/app/views/sessions/show.html.erb b/app/views/sessions/show.html.erb deleted file mode 100644 index 41d87d0e1..000000000 --- a/app/views/sessions/show.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -

Hello <%= Current.user.name %>

- -<%= button_to "Sign out", session_path, method: :delete %> diff --git a/app/views/splats/index.html.erb b/app/views/splats/index.html.erb index 0c5c7da0b..3b9beef92 100644 --- a/app/views/splats/index.html.erb +++ b/app/views/splats/index.html.erb @@ -1,9 +1,9 @@ <% content_for :header do %>