diff --git a/app/assets/stylesheets/components/navigation.css b/app/assets/stylesheets/components/navigation.css index b07cade..7220b6e 100644 --- a/app/assets/stylesheets/components/navigation.css +++ b/app/assets/stylesheets/components/navigation.css @@ -197,12 +197,8 @@ } .nav-overflow-dropdown { - display: none; - position: relative; -} - -.nav-overflow-dropdown.is-visible { display: inline-flex; + position: relative; } .nav-overflow-toggle { diff --git a/app/assets/stylesheets/components/settings.css b/app/assets/stylesheets/components/settings.css index 5df30a3..ea28d6e 100644 --- a/app/assets/stylesheets/components/settings.css +++ b/app/assets/stylesheets/components/settings.css @@ -118,6 +118,57 @@ border-color: var(--color-border); } +.theme-choice { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-6); + padding: var(--spacing-3) var(--spacing-4); + border-radius: 999px; + background: var(--color-bg-card); + border: var(--border-width) solid var(--color-border); +} + +.theme-choice-label { + font-weight: var(--font-weight-medium); + color: var(--color-text-primary); +} + +.theme-choice-buttons { + display: inline-flex; + align-items: center; + gap: var(--spacing-2); +} + +.theme-choice-button { + display: inline-flex; + align-items: center; + gap: var(--spacing-2); + padding: var(--spacing-2) var(--spacing-4); + border-radius: 999px; + border: var(--border-width) solid var(--color-border); + background: var(--color-bg-page); + color: var(--color-text-secondary); + cursor: pointer; + transition: background-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast); +} + +.theme-choice-button:hover { + background: var(--color-bg-hover); + box-shadow: var(--shadow-sm); + transform: translateY(-1px); +} + +.theme-choice-button.is-active { + background: var(--color-bg-button); + color: var(--color-text-button); + border-color: transparent; +} + +.theme-choice-button.is-active i { + color: currentColor; +} + .settings-page .button-secondary:hover { border-color: var(--color-primary-300); box-shadow: var(--shadow-sm); diff --git a/app/assets/stylesheets/components/sidebar.css b/app/assets/stylesheets/components/sidebar.css index e418190..b5f4903 100644 --- a/app/assets/stylesheets/components/sidebar.css +++ b/app/assets/stylesheets/components/sidebar.css @@ -166,6 +166,10 @@ } } +.sidebar .tags-list { + padding-left: var(--spacing-3); +} + .sidebar .attachments-list { list-style: none; padding-left: 0; diff --git a/app/javascript/controllers/nav_dropdown_controller.js b/app/javascript/controllers/nav_dropdown_controller.js new file mode 100644 index 0000000..1ff9c6b --- /dev/null +++ b/app/javascript/controllers/nav_dropdown_controller.js @@ -0,0 +1,18 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + this.onDocumentClick = this.handleDocumentClick.bind(this) + document.addEventListener("click", this.onDocumentClick) + } + + disconnect() { + document.removeEventListener("click", this.onDocumentClick) + } + + handleDocumentClick(event) { + if (!this.element.open) return + if (this.element.contains(event.target)) return + this.element.removeAttribute("open") + } +} diff --git a/app/javascript/controllers/theme_controller.js b/app/javascript/controllers/theme_controller.js index 50971b3..2ad6768 100644 --- a/app/javascript/controllers/theme_controller.js +++ b/app/javascript/controllers/theme_controller.js @@ -4,18 +4,26 @@ const STORAGE_KEY = "hackorum-theme" const DEFAULT_THEME = "light" export default class extends Controller { - static targets = ["icon", "label"] + static targets = ["icon", "label", "button"] connect() { this.applyInitialTheme() } toggle(event) { - event.preventDefault() + if (event && event.type !== "change") { + event.preventDefault() + } const nextTheme = this.currentTheme === "dark" ? "light" : "dark" this.setTheme(nextTheme) } + select(event) { + event.preventDefault() + const { themeValue } = event.currentTarget.dataset + this.setTheme(themeValue) + } + applyInitialTheme() { const storedTheme = window.localStorage.getItem(STORAGE_KEY) const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches @@ -47,6 +55,12 @@ export default class extends Controller { this.element.setAttribute("aria-pressed", theme === "dark") this.element.setAttribute("title", `Switch to ${theme === "dark" ? "light" : "dark"} mode`) + + if (this.hasButtonTarget) { + this.buttonTargets.forEach((button) => { + button.classList.toggle("is-active", button.dataset.themeValue === theme) + }) + } } get currentTheme() { diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index fd778c9..d788fa9 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -43,7 +43,7 @@ html data-theme="light" span Please set a username in Settings. - starred_active = controller_name == "topics" && action_name == "index" && params[:filter].to_s == "starred_by_me" nav.main-navigation - .nav-container data-controller="nav-overflow" data-nav-overflow-target="container" + .nav-container .nav-brand - if content_for?(:sidebar) button.nav-burger type="button" aria-label="Toggle sidebar" data-action="click->sidebar#toggleMobile" @@ -68,9 +68,6 @@ html data-theme="light" = link_to "Statistics", stats_path, class: "nav-link" = link_to "Reports", reports_path, class: "nav-link" = link_to "Help", help_index_path, class: "nav-link" - button.nav-link.theme-toggle type="button" aria-label="Toggle theme" data-controller="theme" data-action="click->theme#toggle" - i.fas.fa-moon data-theme-target="icon" - span data-theme-target="label" Theme - if user_signed_in? - if current_user&.person&.default_alias = link_to current_user.person.default_alias.name, person_path(current_user.person.default_alias.email), class: "nav-link nav-user" @@ -93,38 +90,39 @@ html data-theme="light" i.fa-regular.fa-bell - if unread.positive? span.nav-badge = unread - .nav-menu data-nav-overflow-target="menu" + .nav-menu .nav-links - = link_to "Topics", topics_path, class: "nav-link", data: { "nav-overflow-target": "item" } + = link_to "Topics", topics_path, class: "nav-link" - search_link = content_for?(:search_sidebar) ? "#search" : topics_path(anchor: "search") - = link_to "Search", search_link, class: "nav-link", data: { "nav-overflow-target": "item" } - = link_to "Statistics", stats_path, class: "nav-link", data: { "nav-overflow-target": "item" } - = link_to "Reports", reports_path, class: "nav-link", data: { "nav-overflow-target": "item" } - = link_to "Help", help_index_path, class: "nav-link", data: { "nav-overflow-target": "item" } + = link_to "Search", search_link, class: "nav-link" + = link_to "Reports", reports_path, class: "nav-link" .nav-right - button.nav-link.theme-toggle type="button" aria-label="Toggle theme" data-controller="theme" data-action="click->theme#toggle" data-nav-overflow-target="item" - i.fas.fa-moon data-theme-target="icon" - span data-theme-target="label" Theme .nav-auth - if user_signed_in? - if current_user&.person&.default_alias - = link_to current_user.person.default_alias.name, person_path(current_user.person.default_alias.email), class: "nav-link nav-user", data: { "nav-overflow-target": "item" } + = link_to current_user.person.default_alias.name, person_path(current_user.person.default_alias.email), class: "nav-link nav-user" - unread = activity_unread_count - = link_to activities_path, class: "nav-link nav-link-activity", title: "Activity", data: { "nav-overflow-target": "item" } do + = link_to activities_path, class: "nav-link nav-link-activity", title: "Activity" do i.fa-regular.fa-bell - if unread.positive? span.nav-badge = unread - = link_to "Settings", settings_root_path, class: "nav-link", data: { "nav-overflow-target": "item" } - - if current_admin? - = link_to "Admin", admin_root_path, class: "nav-link", data: { "nav-overflow-target": "item" } - = button_to "Sign out", session_path, method: :delete, class: "nav-link", form: { style: 'display:inline', data: { "nav-overflow-target": "item" } }, data: { turbo: false } - else - = link_to "Sign in", new_session_path, class: "nav-link", data: { "nav-overflow-target": "item" } - = link_to "Register", new_registration_path, class: "nav-link", data: { "nav-overflow-target": "item" } - details.nav-overflow-dropdown data-nav-overflow-target="overflow" - summary.nav-link.nav-overflow-toggle aria-label="More" data-action="click->sidebar#closeMenuOnNavigate" + = link_to "Sign in", new_session_path, class: "nav-link" + = link_to "Register", new_registration_path, class: "nav-link" + details.nav-overflow-dropdown data-controller="nav-dropdown" + summary.nav-link.nav-overflow-toggle aria-label="Menu" data-action="click->sidebar#closeMenuOnNavigate" i.fa-solid.fa-bars - .nav-overflow-menu data-nav-overflow-target="overflowMenu" + .nav-overflow-menu + = link_to "Statistics", stats_path, class: "nav-link" + = link_to "Help", help_index_path, class: "nav-link" + - if user_signed_in? + = link_to "Settings", settings_root_path, class: "nav-link" + - if current_admin? + = link_to "Admin", admin_root_path, class: "nav-link" + = button_to "Sign out", session_path, method: :delete, class: "nav-link", form: { style: 'display:inline' }, data: { turbo: false } + - else + = link_to "Sign in", new_session_path, class: "nav-link" + = link_to "Register", new_registration_path, class: "nav-link" - if content_for?(:sidebar) .page-layout.with-sidebar data-sidebar-target="layout" diff --git a/app/views/settings/profiles/show.html.slim b/app/views/settings/profiles/show.html.slim index 6bdc939..ad71081 100644 --- a/app/views/settings/profiles/show.html.slim +++ b/app/views/settings/profiles/show.html.slim @@ -11,6 +11,19 @@ = f.text_field :username, placeholder: "your_name", value: current_user.username = f.submit "Save username", class: "button-primary" + .settings-section + h2 Appearance + p.settings-hint Choose your preferred theme. + .theme-choice data-controller="theme" + span.theme-choice-label Theme + .theme-choice-buttons + button.theme-choice-button type="button" data-theme-value="light" data-theme-target="button" data-action="click->theme#select" + i.fa-solid.fa-sun aria-hidden="true" + span Light + button.theme-choice-button type="button" data-theme-value="dark" data-theme-target="button" data-action="click->theme#select" + i.fa-regular.fa-moon aria-hidden="true" + span Dark + .settings-section h2 Mention Settings p.settings-hint Control who can @mention you in notes. diff --git a/app/views/topics/_sidebar.html.slim b/app/views/topics/_sidebar.html.slim index 36f29bf..00de185 100644 --- a/app/views/topics/_sidebar.html.slim +++ b/app/views/topics/_sidebar.html.slim @@ -37,6 +37,6 @@ - if user_signed_in? && @available_note_tags.present? .sidebar-section h3.sidebar-heading My tags - ul.quick-filters + ul.quick-filters.tags-list - @available_note_tags.each do |tag, count| li = link_to "##{tag} (#{count})", topics_path(note_tag: tag)