diff --git a/Gemfile.lock b/Gemfile.lock
index 91da8a5b6..16dca130d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -359,7 +359,7 @@ GEM
logger (~> 1.6)
letter_opener (1.10.0)
launchy (>= 2.2, < 4)
- lexxy (0.1.9.beta)
+ lexxy (0.1.10.beta)
rails (>= 8.0.2)
lint_roller (1.1.0)
logger (1.7.0)
diff --git a/app/assets/stylesheets/card-columns.css b/app/assets/stylesheets/card-columns.css
index b6753394e..f4ff6ac05 100644
--- a/app/assets/stylesheets/card-columns.css
+++ b/app/assets/stylesheets/card-columns.css
@@ -73,7 +73,7 @@
z-index: 3;
}
- &:not(.cards--considering, .is-collapsed) {
+ &:not(.cards--considering, .is-collapsed, .cards--grid) {
overflow: clip;
}
diff --git a/app/assets/stylesheets/lexxy.css b/app/assets/stylesheets/lexxy.css
index f2dd41d2e..63f5f1f14 100644
--- a/app/assets/stylesheets/lexxy.css
+++ b/app/assets/stylesheets/lexxy.css
@@ -31,12 +31,6 @@
position: absolute;
white-space: pre-line;
}
-
- /* Disable the submit button when Lexical is empty */
- + .comment__submit {
- opacity: 0.5;
- pointer-events: none;
- }
}
}
@@ -178,11 +172,12 @@
border: 1px solid var(--color-ink-lighter);
border-radius: 0.3em;
color: var(--color-ink);
- font-family: var(--font-mono);
+ font-family: var(--font-base);
font-size: var(--text-x-small);
font-weight: 500;
+ inset-inline-end: 0;
line-height: inherit;
- margin: 0.3em 0.3em 0 -0.3em;
+ margin: 0.3em 0.3em 0 0;
padding: 0.5em 1.8em 0.5em 1.2em;
text-align: start;
diff --git a/app/assets/stylesheets/rich-text-content.css b/app/assets/stylesheets/rich-text-content.css
index f73affd41..18641da1a 100644
--- a/app/assets/stylesheets/rich-text-content.css
+++ b/app/assets/stylesheets/rich-text-content.css
@@ -178,7 +178,7 @@
}
.attachment__caption {
- color: var(--color-ink-dark);
+ color: color-mix(in oklch, var(--color-ink) 66%, transparent);
font-size: var(--text-small);
.input {
diff --git a/app/assets/stylesheets/trays.css b/app/assets/stylesheets/trays.css
index 179ac73d4..b024a0412 100644
--- a/app/assets/stylesheets/trays.css
+++ b/app/assets/stylesheets/trays.css
@@ -345,7 +345,30 @@
}
}
- &:nth-child(1n + 11) { display: none; }
+ /* Show 6 max items on smallest devices */
+ @media (max-height: 578px) {
+ &:nth-child(1n + 7) { display: none; }
+ }
+
+ /* 7 max */
+ @media (min-height: 578px) and (max-height: 656px) {
+ &:nth-child(1n + 8) { display: none; }
+ }
+
+ /* 8 max */
+ @media (min-height: 656px) and (max-height: 734px) {
+ &:nth-child(1n + 9) { display: none; }
+ }
+
+ /* 9 max */
+ @media (min-height: 734px) and (max-height: 812px) {
+ &:nth-child(1n + 10) { display: none; }
+ }
+
+ /* 10 max on larger screens */
+ @media (min-height: 812px) {
+ &:nth-child(1n + 11) { display: none; }
+ }
&:not([aria-selected]) .card__link:focus-visible,
.tray__dialog:not([open]) & .card__link:focus-visible {
diff --git a/app/controllers/cards_controller.rb b/app/controllers/cards_controller.rb
index 809428f91..526806879 100644
--- a/app/controllers/cards_controller.rb
+++ b/app/controllers/cards_controller.rb
@@ -27,7 +27,9 @@ class CardsController < ApplicationController
end
def update
- @card.update! card_params
+ suppressing_broadcasts_unless_published(@card) do
+ @card.update! card_params
+ end
if @card.published?
render_card_replacement
@@ -50,6 +52,14 @@ class CardsController < ApplicationController
@card = Current.user.accessible_cards.find params[:id]
end
+ def suppressing_broadcasts_unless_published(card, &block)
+ if card.published?
+ yield
+ else
+ Collection.suppressing_turbo_broadcasts(&block)
+ end
+ end
+
def card_params
params.expect(card: [ :status, :title, :description, :image, tag_ids: [] ])
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index d066632bd..11b793b17 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -24,7 +24,7 @@ class UsersController < ApplicationController
def show
@filter = Current.user.filters.new(creator_ids: [ @user.id ])
- @day_timeline = Current.user.timeline_for(Time.current, filter: @filter)
+ @day_timeline = Current.user.timeline_for(day_param, filter: @filter)
end
def update
@@ -50,6 +50,14 @@ class UsersController < ApplicationController
head :forbidden unless Current.user.can_change?(@user)
end
+ def day_param
+ if params[:day].present?
+ Time.zone.parse(params[:day])
+ else
+ Time.current
+ end
+ end
+
def user_params
params.expect(user: [ :name, :email_address, :password, :avatar ])
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 0eecdb25f..9200f84b7 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -52,17 +52,6 @@ module EventsHelper
tag.div class: "events__time-block", style: "grid-area: #{row}/#{col}", &
end
- def event_next_page_link(next_day)
- if next_day
- tag.div id: "next_page",
- data: { controller: "fetch-on-visible",
- fetch_on_visible_url_value: events_days_path(
- day: next_day.strftime("%Y-%m-%d"),
- **@filter.as_params
- ) }
- end
- end
-
def event_action_sentence(event)
if event.action.comment_created?
comment_event_action_sentence(event)
diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb
index 8f2fc7988..ee665e98a 100644
--- a/app/helpers/pagination_helper.rb
+++ b/app/helpers/pagination_helper.rb
@@ -35,6 +35,30 @@ module PaginationHelper
end
end
+ def with_automatic_pagination(name, page, **properties)
+ pagination_list name, paginate_on_scroll: true, **properties do
+ concat(pagination_frame_tag(name, page) do
+ yield
+ concat link_to_next_page(name, page, activate_when_observed: true)
+ end)
+ end
+ end
+
+ def day_timeline_pagination_frame_tag(day_timeline, &)
+ turbo_frame_tag day_timeline_pagination_frame_id_for(day_timeline.day), data: { timeline_target: "frame" }, role: "presentation", refresh: :morph, &
+ end
+
+ def day_timeline_pagination_frame_id_for(day)
+ "day-timeline-pagination-contents-#{day.strftime("%Y-%m-%d")}"
+ end
+
+ def day_timeline_pagination_link(day_timeline, filter)
+ if day_timeline.next_day
+ link_to "Load more...", events_days_path(day: day_timeline.next_day.strftime("%Y-%m-%d"), **filter.as_params),
+ data: { frame: day_timeline_pagination_frame_id_for(day_timeline.next_day), pagination_target: "paginationLink" }
+ end
+ end
+
private
def pagination_list(name, tag_element: :div, paginate_on_scroll: false, **properties, &block)
classes = properties.delete(:class)
diff --git a/app/javascript/controllers/collapsible_columns_controller.js b/app/javascript/controllers/collapsible_columns_controller.js
index 84ef0d315..5002c46e2 100644
--- a/app/javascript/controllers/collapsible_columns_controller.js
+++ b/app/javascript/controllers/collapsible_columns_controller.js
@@ -14,7 +14,7 @@ export default class extends Controller {
}
preventToggle(event) {
- if (event.detail.attributeName === "class") {
+ if (event.target.hasAttribute("data-collapsible-columns-target") && event.detail.attributeName === "class") {
event.preventDefault()
}
}
diff --git a/app/javascript/controllers/form_controller.js b/app/javascript/controllers/form_controller.js
index 4e59f6055..2b30c99b9 100644
--- a/app/javascript/controllers/form_controller.js
+++ b/app/javascript/controllers/form_controller.js
@@ -1,8 +1,8 @@
import { Controller } from "@hotwired/stimulus"
-import { debounce } from "helpers/timing_helpers";
+import { debounce, nextFrame } from "helpers/timing_helpers";
export default class extends Controller {
- static targets = [ "cancel" ]
+ static targets = [ "cancel", "submit" ]
static values = {
debounceTimeout: { type: Number, default: 300 }
@@ -37,6 +37,16 @@ export default class extends Controller {
event.preventDefault()
}
+ async disableSubmitWhenInvalid(event) {
+ await nextFrame()
+
+ if (this.element.checkValidity()) {
+ this.submitTarget.removeAttribute("disabled")
+ } else {
+ this.submitTarget.toggleAttribute("disabled", true)
+ }
+ }
+
select(event) {
event.target.select()
}
diff --git a/app/views/cards/comments/_new.html.erb b/app/views/cards/comments/_new.html.erb
index 7931cd244..015c93a83 100644
--- a/app/views/cards/comments/_new.html.erb
+++ b/app/views/cards/comments/_new.html.erb
@@ -10,10 +10,10 @@
local_save_key_value: "comment-#{card.id}",
action: "turbo:submit-end->local-save#submit keydown.ctrl+enter->form#submit:prevent keydown.meta+enter->form#submit:prevent keydown.esc->form#cancel:stop" } do |form| %>
<%= form.rich_textarea :body, required: true, placeholder: new_comment_placeholder(card),
- data: { local_save_target: "input", action: "lexxy:change->local-save#save turbo:morph-element->local-save#restoreContent" } do %>
+ data: { local_save_target: "input", action: "lexxy:change->form#disableSubmitWhenInvalid lexxy:change->local-save#save turbo:morph-element->local-save#restoreContent" } do %>
<%= general_prompts(@card.collection) %>
<% end %>
- <%= form.button class: "comment__submit btn btn--reversed flex-item-justify-start" do %>
+ <%= form.button class: "comment__submit btn btn--reversed flex-item-justify-start", data: { form_target: "submit" }, disabled: true do %>
Post this comment
<% end %>
diff --git a/app/views/cards/index.html.erb b/app/views/cards/index.html.erb
index 1e457c452..47d15dcb8 100644
--- a/app/views/cards/index.html.erb
+++ b/app/views/cards/index.html.erb
@@ -27,6 +27,8 @@
<%= turbo_frame_tag :cards_container do %>