diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ec8dce9c4..b3da2e276 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -24,7 +24,7 @@ module ApplicationHelper tag.li(id: id, class: klass) do link_to(url, id: "#{id}-link", **options) do - concat tag.i(class: icon) + concat svg_icon(icon) concat " " concat tag.span(text) end @@ -189,6 +189,21 @@ module ApplicationHelper end end + def simple_avatar(user, **options) + return "" if user.nil? + post_id = user.avatar_id + deferred_post_ids.add(post_id) if post_id + + klass = options.delete(:class) + named = options.delete(:named) + tag.a href: user_path(user), class: "simple-avatar #{klass}", data: { id: post_id, name: user.name } do + tag.span(class: "simple-avatar-button") do + concat tag.span(user.pretty_name, class: "simple-avatar-name") if named + concat tag.span(class: "simple-avatar-image", data: { name: user.name[0].capitalize }) + end + end + end + def user_banner(user) return "" if user.nil? post_id = user.banner_id diff --git a/app/helpers/icon_helper.rb b/app/helpers/icon_helper.rb new file mode 100644 index 000000000..f5f919320 --- /dev/null +++ b/app/helpers/icon_helper.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module IconHelper + # https://lucide.dev/ + PATHS = { + # Navigation + hamburger: %(), + brush: %(), + images: %(), + library: %(), + group: %(), + tags: %(), + megaphone: %(), + message_square: %(), + lectern: %(), + + swatch: %(), + settings: %(), + log_in: %(), + + # Pagination + chevron_left: %(), + chevron_right: %(), + ellipsis: %(), + + # Posts + fullscreen: %(), + }.freeze + + def svg_icon(name, *args) + options = args.extract_options! + width = options[:width] || 24 + height = options[:height] || 24 + + tag.svg( + "xmlns": "http://www.w3.org/2000/svg", + "width": width, + "height": height, + "viewbox": "0 0 24 24", + "fill": "none", + "stroke": "currentColor", + "stroke-width": 2, + "stroke-linecap": "round", + "stroke-linejoin": "round", + ) do + raw(PATHS[name]) # rubocop:disable Rails/OutputSafety + end + end +end diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index 1759c2cd6..2e65d042f 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -2,17 +2,23 @@ module PaginationHelper def sequential_paginator(records) - with_paginator_wrapper do + tag.div(class: "paginator") do return "" if records.try(:none?) html = "".html_safe - unless records.is_first_page? - html << tag.li(link_to("< Previous", nav_params_for("a#{records[0].id}"), rel: "prev", id: "paginator-prev", data: { shortcut: "a left" })) + + # Previous + html << link_to(records.is_first_page? ? "#" : nav_params_for("a#{records[0].id}"), class: "prev", id: "paginator-prev", rel: "prev", data: { shortcut: "a left", disabled: records.is_first_page? }) do + concat svg_icon(:chevron_left) + concat tag.span("Prev") end - unless records.is_last_page? - html << tag.li(link_to("Next >", nav_params_for("b#{records[-1].id}"), rel: "next", id: "paginator-next", data: { shortcut: "d right" })) + # Next + html << link_to(records.is_last_page? ? "#" : nav_params_for("b#{records[-1].id}"), class: "next", id: "paginator-next", rel: "next", data: { shortcut: "d right", disabled: records.is_last_page? }) do + concat tag.span("Next") + concat svg_icon(:chevron_right) end + html end end @@ -22,64 +28,73 @@ module PaginationHelper return sequential_paginator(records) end - with_paginator_wrapper do + tag.div(class: "paginator", data: { total: [records.total_pages, records.max_numbered_pages].min, current: records.current_page }) do html = "".html_safe - icon_left = tag.i(class: "fa-solid fa-chevron-left") - if records.current_page >= 2 - html << tag.li(class: "arrow") { link_to(icon_left, nav_params_for(records.current_page - 1), rel: "prev", id: "paginator-prev", data: { shortcut: "a left" }) } - else - html << tag.li(class: "arrow") { tag.span(icon_left) } + + # Previous + has_prev = records.current_page < 2 + html << link_to(has_prev ? "#" : nav_params_for(records.current_page - 1), class: "prev", id: "paginator-prev", rel: "prev", data: { shortcut: "a left", disabled: has_prev }) do + concat svg_icon(:chevron_left) + concat tag.span("Prev") end - paginator_pages(records).each do |page| - html << numbered_paginator_item(page, records) + # Break + html << tag.div(class: "break") + + # Numbered + paginator_pages(records).each do |page, klass| + html << numbered_paginator_item(page, klass, records) end - icon_right = tag.i(class: "fa-solid fa-chevron-right") - if records.current_page < records.total_pages - html << tag.li(class: "arrow") { link_to(icon_right, nav_params_for(records.current_page + 1), rel: "next", id: "paginator-next", data: { shortcut: "d right" }) } - else - html << tag.li(class: "arrow") { tag.span(icon_right) } + # Next + has_next = records.current_page >= records.total_pages + html << link_to(has_next ? "#" : nav_params_for(records.current_page + 1), class: "next", id: "paginator-next", rel: "next", data: { shortcut: "d right", disabled: has_next }) do + concat tag.span("Next") + concat svg_icon(:chevron_right) end + html end end private - def with_paginator_wrapper(&) - tag.div(class: "paginator") do - tag.menu(&) - end - end - def paginator_pages(records) - window = 4 + small_window = 2 + large_window = 4 last_page = [records.total_pages, records.max_numbered_pages].min - left = [2, records.current_page - window].max - right = [records.current_page + window, last_page - 1].min + left_sm = [2, records.current_page - small_window].max + left_lg = [2, records.current_page - large_window].max + right_sm = [records.current_page + small_window, last_page - 1].min + right_lg = [records.current_page + large_window, last_page - 1].min + small_range = left_sm..right_sm - [ - 1, - ("..." unless left == 2), - (left..right).to_a, - ("..." unless right == last_page - 1), - (last_page unless last_page <= 1), - ].flatten.compact + result = [ + [1, "first"], + ] + result.push([0, "spacer"]) unless left_lg == 2 + (left_lg..right_lg).each do |page| + result.push([page, small_range.member?(page) ? "sm" : "lg"]) + end + result.push([0, "spacer"]) unless right_lg == last_page - 1 + result.push([last_page, "last"]) unless last_page <= 1 + + result end - def numbered_paginator_item(page, records) + def numbered_paginator_item(page, klass, records) return "" if page.to_i > records.max_numbered_pages html = "".html_safe - if page == "..." - html << tag.li(class: "more") { tag.i(class: "fa-solid fa-ellipsis") } + if page == 0 + html << link_to(svg_icon(:ellipsis), nav_params_for(0), class: "spacer") elsif page == records.current_page - html << tag.li(class: "current-page") { tag.span(page) } + html << tag.span(page, class: "page current") else - html << tag.li(class: "numbered-page") { link_to(page, nav_params_for(page)) } + html << link_to(page, nav_params_for(page), class: "page #{klass}") end + html end diff --git a/app/javascript/src/javascripts/home.js b/app/javascript/src/javascripts/home.js new file mode 100644 index 000000000..612c451a0 --- /dev/null +++ b/app/javascript/src/javascripts/home.js @@ -0,0 +1,43 @@ +import Page from "./utility/page"; + +const Home = {}; + +Home.init = function () { + + const $form = $("#home-search-form"); + const $tags = $("#tags"); + + let isEmpty = !$tags.val(); + let wasEmpty = isEmpty; + if (isEmpty) $form.addClass("empty"); + + $tags.on("input", () => { + wasEmpty = isEmpty; + isEmpty = !$tags.val(); + + if (isEmpty && !wasEmpty) $form.addClass("empty"); + else if (!isEmpty && wasEmpty) $form.removeClass("empty"); + }); + + $(".home-buttons a").on("click", (event) => { + if (isEmpty) return; // Act like regular links + + event.preventDefault(); + const extraTags = $(event.currentTarget).attr("tags"); + if (extraTags) { + $tags.val((index, value) => { + return value + " " + extraTags; + }); + } + + $form.trigger("submit"); + return false; + }); +}; + +$(() => { + if (!Page.matches("static", "home")) return; + Home.init(); +}); + +export default Home; diff --git a/app/javascript/src/javascripts/navigation.js b/app/javascript/src/javascripts/navigation.js index 3d5fd84ad..beae52ac3 100644 --- a/app/javascript/src/javascripts/navigation.js +++ b/app/javascript/src/javascripts/navigation.js @@ -2,7 +2,7 @@ const Navigation = {}; Navigation.init = function () { const wrapper = $("html"); - $("#nav-toggle, .nav-offset-left, .nav-offset-bottom").on("click", (event) => { + $("#nav-toggle, .nav-offset-left, .nav-offset-bott").on("click", (event) => { event.preventDefault(); wrapper.toggleClass("nav-toggled"); diff --git a/app/javascript/src/javascripts/news_updates.js b/app/javascript/src/javascripts/news_updates.js index aae44cbd1..aabe7912c 100644 --- a/app/javascript/src/javascripts/news_updates.js +++ b/app/javascript/src/javascripts/news_updates.js @@ -10,7 +10,6 @@ NewsUpdate.initialize = function () { let newsOpen = false; $("#news-header, #news-show").on("click", (event) => { event.preventDefault(); - console.log("click"); newsOpen = !newsOpen; $("#news").toggleClass("open", newsOpen); diff --git a/app/javascript/src/javascripts/paginator.js b/app/javascript/src/javascripts/paginator.js new file mode 100644 index 000000000..434750a2c --- /dev/null +++ b/app/javascript/src/javascripts/paginator.js @@ -0,0 +1,19 @@ +const Paginator = {}; + +Paginator.init_fasttravel = function (button) { + button.on("click", (event) => { + event.preventDefault(); + + const value = prompt("Navigate to page"); + if (!value) return; + + window.location.replace(button.attr("href").replace("page=0", "page=" + value)); + }); +}; + +$(() => { + for (const one of $(".paginator a.spacer").get()) + Paginator.init_fasttravel($(one)); +}); + +export default Paginator; diff --git a/app/javascript/src/javascripts/post_mode_menu.js b/app/javascript/src/javascripts/post_mode_menu.js index a94151fdf..31415a3a8 100644 --- a/app/javascript/src/javascripts/post_mode_menu.js +++ b/app/javascript/src/javascripts/post_mode_menu.js @@ -35,12 +35,10 @@ PostModeMenu.change_tag_script = function (e) { e.preventDefault(); const newScriptID = Number(e.key); - console.log(newScriptID, LStorage.Posts.TagScript.ID); if (!newScriptID || newScriptID == LStorage.Posts.TagScript.ID) return; LStorage.Posts.TagScript.ID = newScriptID; - console.log("settings", LStorage.Posts.TagScript.ID, LStorage.Posts.TagScript.Content); $("#tag-script-field").val(LStorage.Posts.TagScript.Content); PostModeMenu.show_notice(newScriptID); }; diff --git a/app/javascript/src/javascripts/post_search.js b/app/javascript/src/javascripts/post_search.js index 78cb1a5ea..a3e3a20cc 100644 --- a/app/javascript/src/javascripts/post_search.js +++ b/app/javascript/src/javascripts/post_search.js @@ -1,4 +1,5 @@ import LStorage from "./utility/storage"; +import Page from "./utility/page"; const PostSearch = {}; @@ -10,6 +11,8 @@ PostSearch.init = function () { $(".wiki-excerpt").each((index, element) => { PostSearch.initialize_wiki_preview($(element)); }); + + PostSearch.initialize_controls(); }; PostSearch.initialize_input = function ($form) { @@ -39,21 +42,35 @@ PostSearch.initialize_input = function ($form) { PostSearch.initialize_wiki_preview = function ($preview) { let visible = LStorage.Posts.WikiExcerpt; - if (visible) - $preview.removeClass("hidden"); + if (visible) $preview.addClass("open"); + window.setTimeout(() => { // Disable the rollout on first load + $preview.removeClass("loading"); + }, 250); - $($preview.find("a.wiki-excerpt-toggle")).on("click", (event) => { + $($preview.find("h3.wiki-excerpt-toggle")).on("click", (event) => { event.preventDefault(); visible = !visible; - $preview.toggleClass("hidden", !visible); + $preview.toggleClass("open", visible); LStorage.Posts.WikiExcerpt = visible; return false; }); }; +PostSearch.initialize_controls = function () { + let fullscreen = LStorage.Posts.Fullscreen; + $("#search-fullscreen").on("click", () => { + fullscreen = !fullscreen; + $("body").attr("data-st-fullscreen", fullscreen); + LStorage.Posts.Fullscreen = fullscreen; + }); +}; + $(() => { + if (!Page.matches("posts", "index") && !Page.matches("favorites")) + return; + PostSearch.init(); }); diff --git a/app/javascript/src/javascripts/shortcuts.js b/app/javascript/src/javascripts/shortcuts.js index 4e3aa6e56..869c2f136 100644 --- a/app/javascript/src/javascripts/shortcuts.js +++ b/app/javascript/src/javascripts/shortcuts.js @@ -28,6 +28,7 @@ Shortcuts.initialize_data_shortcuts = function () { Shortcuts.keydown(keys, namespace, event => { const e = $(`[data-shortcut="${keys}"]`).get(0); + if ($e.data("disabled")) return; if ($e.is("input, textarea")) { $e.trigger("focus").selectEnd(); } else { diff --git a/app/javascript/src/javascripts/themes.js b/app/javascript/src/javascripts/themes.js index 83d7f9da6..8ee283cfc 100644 --- a/app/javascript/src/javascripts/themes.js +++ b/app/javascript/src/javascripts/themes.js @@ -3,7 +3,7 @@ import LStorage from "./utility/storage"; const Theme = {}; -Theme.Values = ["Main", "Extra", "StickyHeader", "Palette", "Navbar", "Gestures"]; +Theme.Values = ["Main", "Extra", "StickyHeader", "ForumNotif", "Palette", "Navbar", "Gestures"]; for (const one of Theme.Values) { Object.defineProperty(Theme, one, { diff --git a/app/javascript/src/javascripts/thumbnails.js b/app/javascript/src/javascripts/thumbnails.js index c2877b281..2ab79d685 100644 --- a/app/javascript/src/javascripts/thumbnails.js +++ b/app/javascript/src/javascripts/thumbnails.js @@ -8,6 +8,23 @@ Thumbnails.initialize = function () { const posts = $(".post-thumb.placeholder, .thumb-placeholder-link"); const replacedPosts = []; + // Avatar special case + for (const post of $(".simple-avatar")) { + const $post = $(post); + + const postID = $post.data("id"); + if (!postID) continue; + + const postData = postsData[postID]; + if (!postData || !postData["preview_url"]) continue; + + $("") + .attr("src", postData["preview_url"]) + .appendTo($post.find("span.simple-avatar-image")); + continue; + } + + // Reset of the deferred posts for (const post of posts) { const $post = $(post); diff --git a/app/javascript/src/javascripts/utility/storage.js b/app/javascript/src/javascripts/utility/storage.js index 50e2e92d3..84c476ef0 100644 --- a/app/javascript/src/javascripts/utility/storage.js +++ b/app/javascript/src/javascripts/utility/storage.js @@ -65,6 +65,9 @@ LStorage.Theme = { /** @returns {boolean} True if the sticky header is enabled */ StickyHeader: ["theme-sheader", false], + + /** @returns {boolean} True if the forum notification dot is enabled */ + ForumNotif: ["theme-forumnotif", false], }; StorageUtils.bootstrapMany(LStorage.Theme); @@ -85,6 +88,9 @@ LStorage.Posts = { /** @returns {boolean} True if the wiki excerpt should be visible */ WikiExcerpt: ["e6.posts.wiki", true], + + /** @returns {boolean} True if the search should be displayed in fullscreen */ + Fullscreen: ["e6.posts.fusk", false], }; StorageUtils.bootstrapMany(LStorage.Posts); diff --git a/app/javascript/src/styles/base.scss b/app/javascript/src/styles/base.scss index 05920d6db..6ec5cfc2f 100644 --- a/app/javascript/src/styles/base.scss +++ b/app/javascript/src/styles/base.scss @@ -9,6 +9,9 @@ @import "base/links"; @import "base/fontawesome"; +@import "common/standard_variables"; +@import "common/standard_elements"; + @import "common/footer"; @import "common/helper_classes"; @import "common/helper_palette"; diff --git a/app/javascript/src/styles/common/_footer.scss b/app/javascript/src/styles/common/_footer.scss index 79047aa3a..31c2c6935 100644 --- a/app/javascript/src/styles/common/_footer.scss +++ b/app/javascript/src/styles/common/_footer.scss @@ -1,73 +1,84 @@ -footer#page-footer { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 0.5rem 0; - - padding: 0.5rem 0 1rem; - margin: 1rem 0 0; - +footer.footer-wrapper { background: var(--color-foreground); + margin-top: 1rem; + + .footer-grid { + display: grid; + grid-template-columns: min-content min-content; + grid-template-areas: + "logo . " + "left right"; + justify-items: center; + gap: 0.5rem 0; + + width: min-content; + margin: 0 auto; + padding: 1rem; + } + + .footer-logo { + grid-area: logo; + width: 100%; + text-align: right; + + img { + width: 5rem; + height: auto; + + // Aligning the logo with the line below + margin-right: -3.25rem; + + background: themed("color-background") themed("image-background"); + border-radius: 50%; + padding: 0.5rem; + } + } .footer-left, .footer-right { display: flex; flex-flow: column; - gap: 0.25em; - padding: 0 0.5rem; - - font-size: 1.25em; - - a, span { - line-height: 1.25em; - } + box-sizing: border-box; + width: 100%; + gap: 0.25rem; + + font-size: 1rem; + + a, span { white-space: nowrap; } + span { line-height: 1.25em; } } + .footer-left { - align-items: end; - border-right: 1px solid var(--color-section); + grid-area: left; + padding-right: 1rem; + + text-align: right; + border-right: 1px solid var(--color-section-lighten-5); } - - .footer-logo { - grid-column: 1 / -1; - grid-row: 1; - text-align: center; - - img { - width: 5rem; - height: 5rem; - } + .footer-right { + grid-area: right; + padding-left: 1rem; } } -// Desktop -footer#page-footer { - @include window-larger-than(800px) { - grid-template-columns: 1fr min-content 1fr; - - .footer-left, .footer-right { - font-size: unset; - flex-flow: row; - align-items: center; - border: 0; - gap: 0; - - a:not(:last-child)::after { - content: ""; - width: 4px; - height: 4px; - background: white; - display: inline-block; - border-radius: 2px; - margin: 0.125rem 0.5rem; - } - - span.footer-running { display: none; } +// Desktop-ish +footer.footer-wrapper { + @include window-larger-than(450px) { + .footer-grid { + grid-template-areas: "logo left right"; + padding: 1rem 0; } - .footer-left { justify-content: right; } - .footer-logo { - grid-column: unset; - grid-row: unset; + display: flex; + align-items: center; + padding-right: 2rem; + box-sizing: border-box; + + img { + width: 6rem; + margin-right: unset; + } } } -} \ No newline at end of file +} diff --git a/app/javascript/src/styles/common/_standard_elements.scss b/app/javascript/src/styles/common/_standard_elements.scss new file mode 100644 index 000000000..843738416 --- /dev/null +++ b/app/javascript/src/styles/common/_standard_elements.scss @@ -0,0 +1,59 @@ +// Standard button +// Could be applied to either a button or a link +// Semi-expected to have an icon +.st-button { + $button-font-size: st-value(100); + $button-background: themed("color-section-lighten-5"); + $button-background-hover: themed("color-section-lighten-10"); + $button-text-color: themed("color-text"); + + display: flex; + gap: st-value(100) / 4; + border-radius: radius(025); + + // Button final size + // Font 1rem + // Padding 2 * 0.5rem + font-size: st-value(100); + line-height: st-value(100); + padding: st-value(100) / 2; + height: st-value(100) * 2; + + // TODO What if button is on a light background + background: $button-background; + color: $button-text-color; + &:hover { background: $button-background-hover; } + + & > svg { + // Icon should be slightly larger than text, + // with padding to fill the entire button height + height: st-value(100) * 1.5; // 1.5rem + width: st-value(100) * 1.5; // 1.5rem + margin: -#{st-value(100) / 2} 0; // 0.5rem + padding: st-value(100) / 4; // 0.25rem + + border-radius: radius(025); + } + & > span { + text-align: left; + + // Do not overflow text + overflow: hidden; + flex: 1; + } + + // Full width button + &.w100 { width: 100%; } + + &.stealth { + background: none; + padding: (st-value(100) / 2) 0; + + svg { background: $button-background; } + span { color: themed("color-link"); } + &:hover { + svg { background: $button-background-hover; } + span { color: themed("color-link-hover"); } + } + } +} diff --git a/app/javascript/src/styles/common/_standard_variables.scss b/app/javascript/src/styles/common/_standard_variables.scss new file mode 100644 index 000000000..8504ec0b5 --- /dev/null +++ b/app/javascript/src/styles/common/_standard_variables.scss @@ -0,0 +1,21 @@ +@use "sass:map"; + +// Standard variables for typography and UI elements + +$st-values: ( + 000: 0rem, + 025: 0.25rem, + 050: 0.50rem, + 075: 0.75rem, + 100: 1rem, +); + +@function st-value($name) { + @return map-get($map: $st-values, $key: $name); +} + +@function padding($value) { @return st-value(($value)); } +@mixin st-padding($value) { padding: padding(($value)); } + +@function radius($value) { @return st-value($value); } +@mixin st-radius($value) { border-radius: radius($value); } diff --git a/app/javascript/src/styles/common/navigation.scss b/app/javascript/src/styles/common/navigation.scss index 3a732f95d..6e383dcd1 100644 --- a/app/javascript/src/styles/common/navigation.scss +++ b/app/javascript/src/styles/common/navigation.scss @@ -6,6 +6,7 @@ nav.navigation { width: 100%; // otherwise narrow when fixed z-index: 20; // otherwise post labels layered above + position: relative; /* Top bar, always visible */ @@ -13,16 +14,18 @@ nav.navigation { grid-area: logo; background-color: themed("color-background"); + height: 3.75rem; a.nav-logo-link { display: flex; + box-sizing: border-box; // Height: 3.75rem - // - padding 0.25 * 2 = 0.5 - // - image 3.25 - height: 3.25rem; - width: 3.25rem; - margin: 0.25rem; + // - padding 0.125 * 2 = 0.25 + // - image 3.5 + height: 3.5rem; + width: 4rem; + margin: 0.125rem; background-image: url("main-logo.svg"); background-repeat: no-repeat; @@ -37,160 +40,208 @@ nav.navigation { display: flex; flex-flow: row-reverse; align-items: center; - gap: 0.5em; font-size: 1.15rem; - padding-right: 0.5em; background-color: themed("color-background"); + padding-right: 0.25rem; + height: 3.75rem; // Height: 3.75rem - // - wrapper padding 0.875 * 2 = 1.75 - // - link padding 0.25 * 2 = 0.5 - // - font size 1.5 - padding: 0.875rem; - + // - link padding 0.625 * 2 = 1.25 + // - internal size 2.5 & > a { display: flex; - gap: 0.25em; + padding: 0.625rem 0.5rem; - padding: 0.25rem 0.5rem; - background: themed("color-foreground"); - border-radius: 6px; + & > span { + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + gap: 0.5rem; + + height: 2.5rem; + min-width: 2.5rem; + line-height: 1.5rem; + padding: 0 0.5rem; - & > i { - font-size: 1.5rem; + background: themed("color-foreground"); color: themed("color-link-active"); + border-radius: 0.25rem; + white-space: nowrap; + } + + &:hover > span, &:active > span { background: themed("color-section"); } + &:focus { outline: none; } + } + + a.simple-avatar { + .simple-avatar-button { + padding: 0; + gap: 0; + + .simple-avatar-name { + padding: 0.5rem; + + @include window-smaller-than(32rem) { + display: none; + } + } + + .simple-avatar-image { + display: flex; + justify-content: center; + align-items: center; + position: relative; + + width: 2.5rem; + height: 2.5rem; + + box-sizing: border-box; + border-radius: 0.25rem; + background: themed("color-section"); + + img { + width: 2.5rem; + height: 2.5rem; + + border-radius: 0.25rem; + object-fit: cover; + + z-index: 1; // above the letter + } + + &::before { + content: attr(data-name); + position: absolute; + font-size: 1.5rem; + } + } + + @include window-smaller-than(32rem) { + &.sign-in .simple-avatar-image { + background: themed("color-foreground"); + } + } } } } - /* Prevent toggled menus from being too wide */ - .nav-offset-left { - grid-area: offleft; - display: none; // flex - background: #00000050; - } - - .nav-offset-bottom { - grid-area: offbott; - display: none; // flex - background: #00000050; + /* Offset elements on the left and bottom */ + // Needed to track clicks outside the menu area + @each $name in ("left", "bott") { + .nav-offset-#{$name} { + grid-area: off#{$name}; + display: none; // flex + background: #00000050; + } } /* Toggled menus, hidden by default */ - .nav-primary { - grid-area: primary; - display: none; // flex - flex-flow: column; + // Naming areas + @each $name in (primary, secondary, tools, help) { + .nav-#{$name} { + grid-area: $name; + display: none; - background-color: themed("color-section"); - font-size: 1.5em; + li { + padding: 0; - li { - padding: 0; - a { - display: block; - border-bottom: 1px solid themed("color-foreground"); - padding: 0.5em; + & > a { + display: flex; + align-items: center; + gap: 0.5rem; - // "Comments" is usually the longest and might wrap - white-space: nowrap; + white-space: nowrap; - i { - width: 1.5rem; - color: themed("color-link-active"); - text-align: center; - } - } - - &.current a { background-color: themed("color-foreground"); } - &.forum-updated { - position: relative; - - &::after { - content: ""; - width: 6px; - height: 6px; - border-radius: 3px; - - background: var(--palette-text-red); - - position: absolute; - right: 0.2em; - top: 1em; + svg { + margin: -0.25rem 0; + color: themed("color-link-active"); + } } } } } - .nav-secondary { - grid-area: secondary; - display: none; // flex + // Common top + .nav-primary, .nav-secondary { flex-flow: column; - background-color: themed("color-foreground"); - font-size: 1.35em; - height: 440px; - // Prevent the tools / help buttons from being pushed // way too low on pages with a lot of secondary links - overflow: scroll; + height: 422px; + overflow-y: scroll; + + li a { + justify-content: start; + } + } + + .nav-primary { + background-color: themed("color-section"); + font-size: 1.25rem; + + li > a { + border-bottom: 1px solid themed("color-foreground"); + line-height: 1.25rem; + padding: 1rem 0.5rem; + } + li.current a { background-color: themed("color-foreground"); } + } + + .nav-secondary { + background-color: themed("color-foreground"); + font-size: 1.2rem; li { - padding: 0; - a { - display: block; + & > a { border-bottom: 1px solid themed("color-section"); - padding: 0.5em; + line-height: 1.2rem; + padding: 0.7rem; + + white-space: wrap; // forum menus are long } &.divider { border-bottom: 1px solid themed("color-section"); - height: 0.25em; + height: 0.25rem; } form input[type="text"] { width: 100%; box-sizing: border-box; - // Reduced font size to make the search - // box less claustrophobic - font-size: 1em; - padding: 0.25em 0.5em; + font-size: 1.25rem; + padding: 0.5rem 0.5rem; } } } - .nav-tools { - grid-area: tools; - - display: none; // grid - grid-template-columns: 1fr 1fr; + // Common bottom + .nav-tools, .nav-help { grid-template-rows: min-content; - - padding: 1rem; + font-size: 1.1rem; gap: 1rem; background-color: themed("color-section"); + + li > a { + justify-content: center; + border-radius: 0.25rem; + + padding: 0.7rem 1rem; + line-height: 1.1rem; + } + } + + .nav-tools { + grid-template-columns: 1fr 1fr; border-top: 1px solid themed("color-foreground"); + padding: 1rem 1rem 0.5rem; li { - padding: 0; - + a { background: themed("color-section-lighten-5"); } &.nav-tools-login { grid-column: 1 / -1; } - - & > a { - display: block; - - background: themed("color-section-lighten-5"); - border-radius: 6px; - - font-size: 125%; - padding: 0.5rem 1rem; - text-align: center; - - i { color: themed("color-link-active"); } - } } &.anonymous li.nav-tools-themes { @@ -199,42 +250,19 @@ nav.navigation { } .nav-help { - grid-area: help; - - display: none; // grid grid-template-columns: 1fr 1fr 1fr; - grid-template-rows: min-content; - - padding: 1rem; - gap: 1rem; - - background: themed("color-section"); + padding: 0.5rem 1rem 1rem; li { - padding: 0; - + a { background: themed("color-section-darken-5"); } &.nav-help-discord, &.nav-help-subscribestar { grid-column: 1 / -1; } - & > a { - display: flex; - - background: themed("color-section-darken-5"); - border-radius: 6px; - - font-size: 125%; - padding: 0.5rem 1rem; - - justify-content: center; - align-items: center; - gap: 0.25em; - - img { - height: 1.25em; - margin: -0.5em 0; - } + & > a img { + height: 1.5rem; + margin: -0.2rem 0; } // Hack to put the wiki/help links before discord/sstar on mobile @@ -255,6 +283,29 @@ body[data-th-sheader="true"] nav.navigation { } +// Forum notification +body[data-th-forumnotif="true"] nav.navigation .nav-primary li.forum-updated { + position: relative; + + &::after { + content: ""; + width: 6px; + height: 6px; + border-radius: 3px; + + background: palette("text-red"); + + position: absolute; + right: 0.2rem; + top: 1.25rem; + + @include window-larger-than(800px) { + top: 0.2rem; + } + } +} + + // Mobile toggle html.nav-toggled { @@ -279,14 +330,14 @@ html.nav-toggled { // Allow scrolling when the menu is too long overflow-y: scroll; - .nav-primary, .nav-secondary, .nav-offset-left, .nav-offset-bottom { + .nav-primary, .nav-secondary, .nav-offset-left, .nav-offset-bott { display: flex; } .nav-tools, .nav-help { display: grid; } .nav-primary, .nav-tools, .nav-help { - box-shadow: -1px 0 5px -1px var(--color-background); + box-shadow: -3px 3px 5px -1px themed("color-background"); } } } @@ -294,74 +345,68 @@ html.nav-toggled { // Desktop nav.navigation, html.nav-toggled nav.navigation { - @include window-larger-than(800px) { + @include window-larger-than(50rem) { grid-template-areas: - "logo primary help tools " - "logo secondary secondary secondary" - ; - grid-template-columns: min-content min-content minmax(0, 1fr) min-content; - grid-template-rows: 1.75em 2em; + "logo primary help help controls" + "logo secondary secondary tools controls"; + grid-template-columns: min-content min-content minmax(0, 1fr) min-content 3.25rem; + grid-template-rows: 1.5rem 1.75rem; - padding: 0 1em 0.5em; + padding: 0 1rem 0.5rem; box-sizing: border-box; height: unset; - background: var(--color-background); + background: themed("color-background"); overflow-y: hidden; // overrides mobile hack allowing the menu scrolling - .nav-logo { - a.nav-logo-link { margin: 0.25rem 0.5rem 0 0; } + .nav-logo a.nav-logo-link { + height: 3.25rem; + width: 3.25rem; + margin: 0.25rem 0.5rem 0 0; } - .nav-offset-left, .nav-offset-bottom, .nav-controls { display: none; } + .mobile { display: none; } - .nav-primary { + // All link ribbons + .desktop { display: flex; flex-flow: row; - background: unset; - font-size: 1.05em; - padding: 0 0.25em; + font-size: 0.875rem; + box-shadow: unset; - li { + li a { display: flex; + align-items: center; + height: 100%; - a { - align-content: center; + padding: 0 0.625rem; + white-space: nowrap; + } + } - border-bottom: 0; - padding: 0 0.75em; - i { display: none; } - } + .nav-primary { + background: unset; + height: unset; + padding-left: 0.25rem; - &.forum-updated::after { - top: 0.2em; - } + li a { + border-bottom: 0; + svg { display: none; } } } .nav-secondary { - display: flex; - flex-flow: row; height: unset; - padding: 0 0.25em; - font-size: 1.05em; - border-radius: 6px; + padding: 0 0.25rem; + border-radius: 0.25rem 0 0 0.25rem; - // Silly fix for too many links - overflow: hidden; + overflow: hidden; // Silly fix for too many links + z-index: 1; // above the avatar li { - display: flex; - - a { - align-content: center; - - border-bottom: 0; - padding: 0 0.75em; - white-space: nowrap; - } + a { border-bottom: 0; } &.divider { display: flex; @@ -371,50 +416,46 @@ nav.navigation, html.nav-toggled nav.navigation { &::after { content: "|"; } } - form { - display: flex; - align-items: center; - - input[type="text"] { min-width: 10em; } + form input[type="text"] { + width: 12rem; + padding: 0.25rem 0.5rem; + font-size: 1rem; + border-radius: 0.25rem; } } } .nav-tools, .nav-help { - display: flex; - padding: 0; background: unset; border: none; gap: 0; - li { - display: flex; + li a { + gap: 0.25rem; - a { - align-content: center; - - background: unset; - font-size: 1.05em; - padding: 0 0.75em; - text-align: unset; - white-space: nowrap; - border-radius: 0; - } + background: unset; + text-align: unset; + border-radius: 0; } } .nav-tools { - - // Otherwise help gets layered above it - // When the viewport is narrow (but not mobile) + background: themed("color-foreground"); z-index: 1; - background: var(--color-background); + border-radius: 0 0.25rem 0.25rem 0; + margin-right: 0.25rem; li { a { - i { color: themed("color-link"); } - &:hover i { color: themed("color-link-hover"); } + padding: 0 0.5rem; + + svg { + color: themed("color-link"); + height: 1.25rem; + width: 1.25rem; + } + &:hover svg { color: themed("color-link-hover"); } } &.nav-tools-themes, &.nav-tools-settings { @@ -422,26 +463,123 @@ nav.navigation, html.nav-toggled nav.navigation { } } } - .nav-help { - li a img { display: none; } + .nav-help { + + // At small resolutions, overflow can + // cause scrollbars to appear + overflow: hidden; + + li a img { display: none; } li.current a { background-color: themed("color-foreground"); } } - .nav-primary, .nav-tools, .nav-help { box-shadow: unset; } + .nav-controls { + position: absolute; + right: 0; + top: 0; + + height: 3.25rem; + box-sizing: border-box; + padding: 0.25rem 0 0; + + #nav-toggle { display: none; } + + a.simple-avatar { + padding: 0; + height: 100%; + + .simple-avatar-button { + background: none; + color: inherit; + align-items: start; + font-size: 0.875rem; + line-height: 0.875rem; + + .simple-avatar-name { + padding: 0 0.5rem; + } + + .simple-avatar-image { + height: 3rem; + width: 3rem; + background: themed("color-foreground"); + + img { + width: 2.75rem; + height: 2.75rem; + } + } + } + } + } + + // Icon collapse + // Stage 1: discord and subscribestar buttons + .collapse-1 { display: none; } + @include window-larger-than(77rem) { + .collapse-1 { display: flex; } + } + + // Stage 2: account label + .collapse-2 .simple-avatar-name { display: none; } + @include window-larger-than(65rem) { + .collapse-2 .simple-avatar-name { display: unset; } + } } } // Tweak for the secondary menu on desktop body.c-static.a-home { - @include window-larger-than(800px) { - nav.navigation, menu.nav-logo, menu.nav-secondary { + @include window-larger-than(50rem) { + + nav.navigation { + + // Center and align the navbar + grid-template-areas: "logo primary help controls"; + grid-template-columns: repeat(4, min-content); + justify-content: center; + + // Remove padding to prevent a scrollbar + // at low desktop resolutions + padding: 0 0 0.5rem; + + #nav-subscribestar, #nav-discord, .nav-secondary, .nav-tools { display: none; } + } + + // Match the background colors + nav.navigation, menu.nav-logo, menu.nav-secondary, menu.nav-controls { background: unset; } menu.nav-tools { background: var(--bg-color); } + menu.nav-controls { + position: static; + height: unset; + padding: 0; + .simple-avatar-button { + height: unset; + align-items: center; + .simple-avatar-image { display: none; } + } + } + } + + @include window-smaller-than(50rem) { + // Only show the primary navbar on mobile + // since the secondary is empty anyways + nav.navigation { + grid-template-areas: + "logo logo controls" + "offleft primary primary " + "offleft tools tools " + "offleft help help " + "offleft offbott offbott "; + + .nav-secondary { display: none; } + } } } diff --git a/app/javascript/src/styles/common/news.scss b/app/javascript/src/styles/common/news.scss index d57876385..180900cd9 100644 --- a/app/javascript/src/styles/common/news.scss +++ b/app/javascript/src/styles/common/news.scss @@ -17,6 +17,8 @@ div#page div#news { &.open { max-height: none; + + #news-body { pointer-events: unset; } } #news-header { @@ -26,26 +28,27 @@ div#page div#news { cursor: pointer; width: 100%; text-align: center; - padding: 0.5rem 0.5rem 0; + padding: 0.5rem; } #news-body { display: block; box-sizing: border-box; - margin-top: 5px; max-width: 800px; padding: 0 0.5rem 0.5rem; + + pointer-events: none; } #news-dismiss { position: absolute; - right: 0.5rem; - top: 0.25rem; + right: 0; + top: 0; + padding: 0.25rem 1rem; cursor: pointer; - font-size: 1.15rem; + font-size: 1.25rem; font-weight: bold; - color: white; } } diff --git a/app/javascript/src/styles/common/paginator.scss b/app/javascript/src/styles/common/paginator.scss index c006084b6..6ffd9a446 100644 --- a/app/javascript/src/styles/common/paginator.scss +++ b/app/javascript/src/styles/common/paginator.scss @@ -1,35 +1,101 @@ +.paginator { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + background-color: themed("color-foreground"); + border-radius: 0.25rem; -div.paginator { - display: block; - padding: 2em 0 1em 0; - text-align: center; - clear: both; + width: max-content; + margin: 0 auto; - menu { + & > a, & > span { display: flex; + box-sizing: border-box; justify-content: center; + align-items: center; + min-width: 2.15rem; + + font-size: 1rem; + line-height: 1rem; + padding: 0.75rem 0.3rem; // otherwise large page numbers wrap + + border-radius: 0.25rem; + + &:hover { + background: themed("color-section"); + } } - li { - a { - margin: 0 0.25em; - padding: 0.25em 0.75em; + & > a[data-disabled="true"] { + color: var(--color-text); + pointer-events: none; + } + + // Ordering + // Oh boy + .page { + order: 20; + &.lg { display: none; } + &.current { cursor: default; } + } + .prev { + order: 1; + margin-right: auto; + } + .spacer { + order: 20; + padding: 0; + + &:last-child { display: none; } + svg { + height: 1rem; + transform: rotate(90deg); + } + } + .next { + order: 9; + margin-left: auto; + } + .break { + order: 10; + width: 100%; + } + + + // Tablet + @include window-larger-than(35rem) { + justify-content: center; + gap: 0.125rem; + + a, span { + order: 0 !important; + min-width: 2.25rem; + font-size: 0.9rem; } - a:hover { - background: $paginator-hover-background; - color: $paginator-hover-color; + .break { display: none; } + .spacer { + padding: inherit; + svg { transform: unset; } } - &.more { - color: $paginator-more-color; + .prev { margin-right: 1rem; } + .next { margin-left: 1rem; } + } + + @include window-larger-than(50rem) { + a, span { + padding: 0.75rem 0.5rem; + font-size: 0.95rem; } - span { - margin: 0 0.25em; - padding: 0.25em 0.75em; - font-weight: bold; + .prev, .next { + span { display: none; } } } + + @include window-larger-than(65rem) { + a.page.lg { display: flex; } + } } diff --git a/app/javascript/src/styles/common/z_responsive.scss b/app/javascript/src/styles/common/z_responsive.scss index df89c65fe..316b3be09 100644 --- a/app/javascript/src/styles/common/z_responsive.scss +++ b/app/javascript/src/styles/common/z_responsive.scss @@ -59,14 +59,6 @@ } } - .guest-warning { - .guest-warning-dialog { - top: 5vh; - height: 90vh; - width: 80vw; - } - } - div#page { > div /* div#c-$controller */ diff --git a/app/javascript/src/styles/specific/guest_warning.scss b/app/javascript/src/styles/specific/guest_warning.scss index 36701b901..be988bd31 100644 --- a/app/javascript/src/styles/specific/guest_warning.scss +++ b/app/javascript/src/styles/specific/guest_warning.scss @@ -1,33 +1,55 @@ .guest-warning { position: fixed; - left: 0; top: 0; - height: 100vh; - width: 100vw; - z-index: 500; - background-color: themed("color-background"); + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + + display: flex; + justify-content: center; + + background: themed("color-background"); .guest-warning-dialog { - z-index: 500; - position: relative; - top: 20vh; - margin-left: auto; - margin-right: auto; - height: 40vh; - width: 40vw; - overflow-x: scroll; - background-color: themed("color-section"); + margin: 20vh 0.5em 0; + + height: min-content; + max-width: 360px; .dialog-header { - padding: $padding-050 $base-padding; - background-color: themed("color-section-lighten-5"); - border-bottom: 1px solid themed("color-background"); + padding: 0 0.5em; + margin-bottom: 0.5em; } + .dialog-content { - padding: $padding-050 $base-padding; + background: var(--color-foreground); + padding: 2em 1em 1em; + border-radius: 3px 3px 0 0; + + p:last-child { margin-bottom: 0; } } + .dialog-footer { - padding: $padding-050 $base-padding; + display: flex; + flex-wrap: wrap; + gap: 0.5em; + + background: var(--color-foreground); + padding: 1em 1em 2em; + border-radius: 0 0 3px 3px; + + button { + background: var(--color-section-lighten-5); + color: var(--color-link); + padding: 0.5em 1em; + border-radius: 3px; + + &:hover { + background: var(--color-section-lighten-10); + color: var(--color-link-hover); + } + } } } } diff --git a/app/javascript/src/styles/specific/home.scss b/app/javascript/src/styles/specific/home.scss index 0bd86c9f1..6bcb8752d 100644 --- a/app/javascript/src/styles/specific/home.scss +++ b/app/javascript/src/styles/specific/home.scss @@ -3,6 +3,12 @@ body.c-static.a-home { background-color: var(--bg-color); background-image: var(--bg-image); + background-position-y: 3.75rem; + + @include window-larger-than(800px) { + background-position-y: unset; + } + #page { background: none; margin: 8rem auto 0; @@ -20,7 +26,7 @@ body.c-static.a-home { padding: 1rem; margin: 0.5rem; - border-radius: 0.5rem; + border-radius: 0.25rem; text-align: center; } @@ -73,6 +79,10 @@ body.c-static.a-home { } } + #home-search-form:not(.empty) .home-buttons a span::before { + content: "Search "; + } + // Footer .home-footer-top { @@ -86,7 +96,7 @@ body.c-static.a-home { justify-content: space-between; align-items: center; - border-radius: 0.5rem 0.5rem 0 0; + border-radius: 0.25rem 0.25rem 0 0; @include window-larger-than(480px) { margin: 0.5rem 0.5rem 0; @@ -109,7 +119,7 @@ body.c-static.a-home { margin: 0; padding: 0.5rem; - border-radius: 0 0 0.5rem 0.5rem; + border-radius: 0 0 0.25rem 0.25rem; @include window-larger-than(480px) { margin: 0 0.5rem; diff --git a/app/javascript/src/styles/specific/post_index.scss b/app/javascript/src/styles/specific/post_index.scss index 497115070..67b6bfcfa 100644 --- a/app/javascript/src/styles/specific/post_index.scss +++ b/app/javascript/src/styles/specific/post_index.scss @@ -1,3 +1,22 @@ +body.c-posts.a-index, body.c-favorites.a-index { + #page { + // Override the theme to instead + // project it upon the content area + background: none; + padding: 0; + } + + // Exhibit A + // Makes the content area take up the + // full height of the page. Yes, really. + #page, #c-posts, #c-favorites, #a-index { + // I hate both this and myself + display: flex; + flex-flow: column; + flex: 1; + } +} + .post-index { display: grid; @@ -7,24 +26,46 @@ "sidebar"; grid-template-columns: 1fr; grid-template-rows: min-content 1fr min-content; - gap: 1em; + + flex: 1; // See Exhibit A // 1. Searchbox .search { grid-area: search; + padding: 0.5rem 0.25rem; + background-color: #152f56; + background-color: themed("color-foreground"); + box-shadow: inset 0px -0.25rem 0.25rem -0.25rem themed("color-background"); + h1 { font-size: $h3-size; } + + .search-controls { + display: none; + flex-flow: column; + } } // 2. Content .content { + display: flex; // See Exhibit A + flex-flow: column; + grid-area: content; + // Imported from #page + padding: 0.5rem 0.25rem themed("content-padding-bottom"); + background-color: #152f56; + background-color: themed("color-foreground"); + background-image: themed("image-foreground"); + background-position: themed("image-foreground-position"); + background-repeat: themed("image-foreground-repeat"); + // Quick tag edit #edit-dialog textarea { - margin-bottom: 0.25em; + margin-bottom: 0.25rem; } // Actual content area: @@ -32,86 +73,13 @@ .post-index-gallery { display: flex; flex-flow: column; - gap: 1em; + gap: 1rem; - .wiki-excerpt { - display: flex; - flex-flow: column; - position: relative; - padding: 1em 1em 0; - gap: 0.5em; + flex: 1; // See Exhibit A - background: var(--color-section); - max-width: 60em; - - .wiki-excerpt-toggle { - position: absolute; - top: 0; - right: 0; - padding: 1em; - outline: none; - - transition: transform 0.25s; - - &::after { - @include font-awesome-icon; - content: unicode("f0d8"); - } - } - - .styled-dtext { - background: linear-gradient(to top, var(--color-section), var(--color-text)); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - max-height: 10em; - overflow: hidden; - - transition: max-height 0.25s; - - // Disable links - pointer-events: none; - cursor: unset; - - a { - color: unset; - text-decoration: underline; - &::after { content: none; } - } - } - - .wiki-excerpt-readmore { - display: flex; - justify-content: center; - align-items: center; - - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 3em; - - // Makes the button appear in the middle of the animation - transition: visibility 0s 0.125s; - - span { - padding: 0.5em 1em; - background: var(--color-section); - border-radius: 6px; - } - } - - &.hidden{ - .wiki-excerpt-toggle { transform: rotate(-90deg); } - .styled-dtext { - max-height: 0; - } - .wiki-excerpt-readmore { visibility: hidden; } - } - } - - .paginator { - padding: 1em 0; + .posts-container { + flex: 1; // See Exhibit A + grid-auto-rows: min-content; } } } @@ -124,6 +92,11 @@ flex-flow: column; gap: 1em; + padding: 0.5rem 0.25rem; + background-color: #152f56; + background-color: themed("color-foreground"); + box-shadow: inset 0px 0.25rem 0.25rem -0.25rem themed("color-background"); + // Mode selection #mode-box-mode, #mode-box #set-id { width: 100%; @@ -139,11 +112,170 @@ // Desktop .post-index { - @include window-larger-than(800px) { + @include window-larger-than(50rem) { grid-template-areas: "search content" "sidebar content"; - grid-template-columns: 15em 1fr; + grid-template-columns: 14rem 1fr; grid-template-rows: min-content 1fr; + + .search { + box-shadow: inset -0.25rem 0px 0.25rem -0.25rem themed("color-background"); + margin-top: 0.25rem; + border-top-left-radius: 0.25rem; + padding: 0.5rem; + + .search-controls { + display: flex; + margin-top: 0.5rem; + } + } + + .sidebar { + box-shadow: inset -0.25rem 0px 0.25rem -0.25rem themed("color-background"); + margin-bottom: 0.25rem; + border-bottom-left-radius: 0.25rem; + padding: 0.5rem + } + + .content { + border-radius: 0.25rem; + } + } +} + + +// Fullscreen +body.c-posts.a-index[data-st-fullscreen="true"] { + // Desktop-only, for obvious reasons + @include window-larger-than(50rem) { + .post-index { + grid-template-areas: + "search " + "content"; + grid-template-columns: 1fr; + + .search { + display: flex; + + border-radius: 0.25rem 0.25rem 0 0; + box-shadow: inset 0px -0.25rem 0.25rem -0.25rem themed("color-background"); + + .post-search { + flex: 1; + } + + .search-controls { + display: flex; + justify-content: right; + align-self: end; + margin: 0 0 0 0.5rem; + + .st-button.w100 { + width: unset; + span { display: none; } + } + } + } + .sidebar { display: none; } + .content { + border-radius: 0 0 0.25rem 0.25rem; + } + } + } +} + + +// FEATURES +// Wiki Excerpt +.wiki-excerpt { + display: flex; + flex-flow: column; + position: relative; + + background: themed("color-section"); + border-radius: 0.25rem; + + // header + h3 { + cursor: pointer; + padding: 0.5rem 1rem 0.5rem 1.5rem; + + &::after { + @include font-awesome-icon; + content: unicode("f0da"); + + transition: transform 0.25s; + + position: absolute; + top: 0; + left: 0; + padding: 0.5rem; + } + } + + // body + .styled-dtext { + background: linear-gradient(to top, themed("color-section"), themed("color-text")); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + + max-height: 0rem; + max-width: 50rem; + overflow: hidden; + padding: 0 0.5rem; + + transition: max-height 0.25s; + + // Disable links + pointer-events: none; + cursor: unset; + + a { + color: unset; + text-decoration: underline; + &::after { content: none; } + } + + p:last-child { margin-bottom: 0; } + } + + // wiki link + .wiki-excerpt-readmore { + display: flex; + justify-content: center; + align-items: center; + + position: absolute; + bottom: 0; + left: 0; + right: 0; + + height: 3rem; + max-width: 50rem; + box-sizing: border-box; + + // Makes the button appear in the middle of the animation + transition: visibility 0s 0.125s; + visibility: hidden; + + span { + padding: 0.5rem 1rem; + background: themed("color-section"); + border-radius: 0.25rem; + } + } + + &.open{ + .wiki-excerpt-toggle::after { transform: rotate(90deg); } + .styled-dtext { + max-height: 10rem; + } + .wiki-excerpt-readmore { visibility: visible; } + } + + &.loading { + h3::after, .styled-dtext { transition: none; } } } diff --git a/app/javascript/src/styles/specific/post_mode_menu.scss b/app/javascript/src/styles/specific/post_mode_menu.scss index 6e02eff31..467f43954 100644 --- a/app/javascript/src/styles/specific/post_mode_menu.scss +++ b/app/javascript/src/styles/specific/post_mode_menu.scss @@ -20,7 +20,7 @@ $modes: ( ); @each $mode, $color in $modes { - #page[data-mode-menu="#{$mode}"] { + #page[data-mode-menu="#{$mode}"] .content { background-color: $color; } } diff --git a/app/javascript/src/styles/specific/posts.scss b/app/javascript/src/styles/specific/posts.scss index 1a73bec76..04d2a4c54 100644 --- a/app/javascript/src/styles/specific/posts.scss +++ b/app/javascript/src/styles/specific/posts.scss @@ -2,6 +2,7 @@ #has-parent-relationship-preview, #has-children-relationship-preview { + display: flex; flex-direction: row; flex-wrap: wrap; diff --git a/app/javascript/src/styles/specific/site_map.scss b/app/javascript/src/styles/specific/site_map.scss index 562e4f6fb..7dfccf9f8 100644 --- a/app/javascript/src/styles/specific/site_map.scss +++ b/app/javascript/src/styles/specific/site_map.scss @@ -1,20 +1,12 @@ - - div#c-static { div#a-site-map { - width: 80em; + display: flex; + flex-flow: row wrap; + max-width: 80em; section { width: 20em; - float: left; - - h1 { - font-size: $h3-size; - } - - ul { - margin-bottom: 1.5em; - } + ul { margin-bottom: 1.5em; } } } } diff --git a/app/javascript/src/styles/specific/users.scss b/app/javascript/src/styles/specific/users.scss index a3bbd9205..0108e4c8c 100644 --- a/app/javascript/src/styles/specific/users.scss +++ b/app/javascript/src/styles/specific/users.scss @@ -249,8 +249,9 @@ div#c-users { div.input { input[type="text"], input[type="email"], input[type="password"], select { - width: 100%; - max-width: unset; + // z_responsive is the absolute worst + width: 100% !important; + max-width: unset !important; box-sizing: border-box; } } diff --git a/app/logical/git_helper.rb b/app/logical/git_helper.rb index 4de133bd1..fe4db750e 100644 --- a/app/logical/git_helper.rb +++ b/app/logical/git_helper.rb @@ -3,18 +3,27 @@ module GitHelper def self.init if Rails.root.join("REVISION").exist? - @hash = Rails.root.join("REVISION").read.strip + @hash = @tag = Rails.root.join("REVISION").read.strip elsif system("type git > /dev/null && git rev-parse --show-toplevel > /dev/null") @hash = `git rev-parse HEAD`.strip + @tag = `git describe --abbrev=0` else - @hash = "" + @hash = @tag = "" end end + def self.tag + @tag + end + def self.hash @hash end + def self.version + @tag.presence || short_hash + end + def self.short_hash @hash[0..8] end @@ -22,4 +31,13 @@ module GitHelper def self.commit_url(commit_hash) "#{Danbooru.config.source_code_url}/commit/#{commit_hash}" end + + def self.release_url(tag_name) + "#{Danbooru.config.source_code_url}/releases/tag/#{tag_name}" + end + + def self.version_url + return release_url(@tag) if @tag.present? + commit_url(@hash) + end end diff --git a/app/models/user_feedback.rb b/app/models/user_feedback.rb index 811994906..4c21ccd74 100644 --- a/app/models/user_feedback.rb +++ b/app/models/user_feedback.rb @@ -38,9 +38,6 @@ class UserFeedback < ApplicationRecord def log_destroy ModAction.log(:user_feedback_destroy, { user_id: user_id, reason: body, type: category, record_id: id }) - deletion_user = "\"#{CurrentUser.name}\":/users/#{CurrentUser.id}" - creator_user = "\"#{creator.name}\":/users/#{creator.id}" - StaffNote.create(body: "#{deletion_user} deleted #{category} feedback, created #{created_at.to_date} by #{creator_user}: #{body}", user_id: user_id, creator: User.system) end end diff --git a/app/views/comments/partials/show/_comment.html.erb b/app/views/comments/partials/show/_comment.html.erb index 5c1db74cd..86aff0df3 100644 --- a/app/views/comments/partials/show/_comment.html.erb +++ b/app/views/comments/partials/show/_comment.html.erb @@ -1,4 +1,4 @@ -<% if comment.should_see?(CurrentUser.user) || (current_page?(:controller => "comments", :action => "show") && CurrentUser.id == comment.creator_id) %> +<% if comment.should_see?(CurrentUser.user) || (params[:controller] == "comments" && params[:action] == "show" && CurrentUser.id == comment.creator_id) %>
" data-post-id="<%= comment.post_id %>" data-comment-id="<%= comment.id %>" data-score="<%= comment.score %>" data-creator="<%= comment.creator&.name.downcase %>" data-is-sticky="<%= comment.is_sticky %>" data-creator-id="<%= comment.creator_id %>" diff --git a/app/views/layouts/_main_links.html.erb b/app/views/layouts/_main_links.html.erb index eea0868fb..58373063a 100644 --- a/app/views/layouts/_main_links.html.erb +++ b/app/views/layouts/_main_links.html.erb @@ -1,7 +1,8 @@ -<%= decorated_nav_link_to("Posts", "fas fa-images", posts_path) %> -<%= decorated_nav_link_to("Pools", "fas fa-book", gallery_pools_path) %> -<%= decorated_nav_link_to("Sets", "fas fa-clone", post_sets_path) %> -<%= decorated_nav_link_to("Tags", "fas fa-tags", tags_path) %> -<%= decorated_nav_link_to("Blips", "fas fa-bullhorn", blips_path) %> -<%= decorated_nav_link_to("Comments", "fas fa-comment-alt", comments_path(group_by: "post")) %> -<%= decorated_nav_link_to("Forum", "fas fa-chalkboard", forum_topics_path, class: (CurrentUser.has_forum_been_updated? ? "forum-updated" : nil)) %> +<%= decorated_nav_link_to("Artists", :brush, artists_path) %> +<%= decorated_nav_link_to("Posts", :images, posts_path) %> +<%= decorated_nav_link_to("Pools", :library, gallery_pools_path) %> +<%= decorated_nav_link_to("Sets", :group, post_sets_path) %> +<%= decorated_nav_link_to("Tags", :tags, tags_path) %> +<%= decorated_nav_link_to("Blips", :megaphone, blips_path) %> +<%= decorated_nav_link_to("Comments", :message_square, comments_path(group_by: "post")) %> +<%= decorated_nav_link_to("Forum", :lectern, forum_topics_path, class: (CurrentUser.has_forum_been_updated? ? "forum-updated" : nil)) %> diff --git a/app/views/layouts/_nav.html.erb b/app/views/layouts/_nav.html.erb index 032db41f6..a8f44f2da 100644 --- a/app/views/layouts/_nav.html.erb +++ b/app/views/layouts/_nav.html.erb @@ -3,45 +3,45 @@ - + <%= render "layouts/main_links" %> - - + + - "> + "> <%= yield :secondary_links %> - - - + + + <%= svg_icon(:hamburger) %> <% if CurrentUser.is_anonymous? %> - <%= link_to(new_session_path, class: "nav-tools-login") do %> - - Sign in - <% end %> + + + <% else %> - <%= link_to(user_path(CurrentUser.user), class: "nav-tools-login") do %> - - Profile - <% end %> + <%= simple_avatar(CurrentUser.user, named: true, class: "nav-controls-profile collapse-2") %> <% end %> - "> - <%= decorated_nav_link_to("Themes", "fas fa-swatchbook", theme_path, class: "nav-tools-themes") %> - <% if CurrentUser.is_anonymous? %> - <%= decorated_nav_link_to("Sign in", "fas fa-sign-in-alt", new_session_path, class: "nav-tools-login") %> - <% else %> - <%= decorated_nav_link_to("Settings", "fas fa-cog", edit_user_path(CurrentUser.user), class: "nav-tools-settings") %> - <%= decorated_nav_link_to("Account #{unread_dmails(CurrentUser.user)}", "far fa-user-circle", home_users_path, class: "nav-tools-login") %> + "> + <%= decorated_nav_link_to("Themes", :swatch, theme_path, class: "nav-tools-themes") %> + <% unless CurrentUser.is_anonymous? %> + <%= decorated_nav_link_to("Settings", :settings, edit_user_path(CurrentUser.user), class: "nav-tools-settings") %> <% end %> - "> + "> <%= nav_link_to("Wiki", wiki_pages_path(title: "help:home"), class: "nav-help-wiki") %> <%= nav_link_to("Help", help_pages_path, class: "nav-help-help") %> <%= nav_link_to("More", site_map_path, class: "nav-help-map") %> diff --git a/app/views/layouts/_theme_include.html.erb b/app/views/layouts/_theme_include.html.erb index e0cd0a058..4a9cfd9f8 100644 --- a/app/views/layouts/_theme_include.html.erb +++ b/app/views/layouts/_theme_include.html.erb @@ -1,17 +1,23 @@ <%= javascript_tag nonce: true do -%> (function() { try { - var theme = localStorage.getItem('theme') || 'hexagon'; - var extra = localStorage.getItem('theme-extra') || 'none'; - var sheader = localStorage.getItem('theme-sheader') || false; - var palette = localStorage.getItem('theme-palette') || 'default'; - var nav = localStorage.getItem('theme-nav') || 'top'; + const values = { + // Theme + "th-main": localStorage.getItem("theme") || "hexagon", + "th-extra": localStorage.getItem("theme-extra") || "none", + "th-sheader": localStorage.getItem("theme-sheader") || false, + "th-forumnotif": localStorage.getItem("theme-forumnotif") || false, + "th-palette": localStorage.getItem("theme-palette") || "default", + "th-nav": localStorage.getItem("theme-nav") || "top", + + // Settings + "st-fullscreen": localStorage.getItem("e6.posts.fusk") || false, + }; + var b = document.body; - b.setAttribute('data-th-main', theme); - b.setAttribute('data-th-extra', extra); - b.setAttribute('data-th-sheader', sheader); - b.setAttribute('data-th-palette', palette); - b.setAttribute('data-th-nav', nav); + for (const [name, value] of Object.entries(values)) { + b.setAttribute("data-" + name, value); + } } catch(e) {} })(); <% end -%> diff --git a/app/views/layouts/blank.html.erb b/app/views/layouts/blank.html.erb index 40b65f21f..2ac1ca296 100644 --- a/app/views/layouts/blank.html.erb +++ b/app/views/layouts/blank.html.erb @@ -10,8 +10,11 @@ <% if CurrentUser.is_anonymous? %> <%= render "static/guest_warning" %> <% end %> +
<%= yield :layout %>
+ + <%= render "static/deferred_posts" %> <% end %> diff --git a/app/views/news_updates/_notice.erb b/app/views/news_updates/_notice.erb index bd9ce5c8b..3da879607 100644 --- a/app/views/news_updates/_notice.erb +++ b/app/views/news_updates/_notice.erb @@ -1,8 +1,8 @@ <% if news_update.present? %> diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb index e1d7b60d5..1d452ab0d 100644 --- a/app/views/posts/index.html.erb +++ b/app/views/posts/index.html.erb @@ -3,6 +3,7 @@