Merge branch 'main' into notification-index

* main:
  Don't round up in time distance expression
  Tidy Gemfile
  Banner for draft state
  Drafts should always use the dashed outline wherever they're displayed
  Unnecessary parens
  Use distance to nearest edge
  Simplify overlap logic
  Fix divider drag jankiness
This commit is contained in:
Jason Zimdars
2025-01-20 13:01:41 -06:00
10 changed files with 69 additions and 35 deletions
+1 -2
View File
@@ -16,6 +16,7 @@ gem "bootsnap", require: false
gem "puma", ">= 5.0"
gem "solid_cable", ">= 3.0"
gem "solid_cache", "~> 1.0"
gem "solid_queue", "~> 1.1"
gem "sqlite3", ">= 2.0"
gem "thruster", require: false
@@ -44,5 +45,3 @@ group :test do
gem "capybara"
gem "selenium-webdriver"
end
gem "solid_queue", "~> 1.1"
+34 -17
View File
@@ -366,18 +366,24 @@
text-decoration: none;
}
li {
border-radius: 0.6em;
list-style: none;
padding: 0.5em var(--inline-space);
position: relative;
transition: background-color 200ms ease-out;
ul {
li {
border-radius: 0.6em;
list-style: none;
padding: 0.5em var(--inline-space);
position: relative;
transition: background-color 200ms ease-out;
}
@media (hover: hover) {
&:hover {
background-color: color(from var(--bubble-color) srgb r g b / 0.15);
border: 0;
border-radius: 0.6em;
&:not(.dragging) {
li {
@media (hover: hover) {
&:hover {
background-color: color(from var(--bubble-color) srgb r g b / 0.15);
border: 0;
border-radius: 0.6em;
}
}
}
}
}
@@ -433,6 +439,10 @@
flex-grow: 1;
white-space: nowrap;
.drafted & {
color: color(from var(--color-ink) srgb r g b / 0.5);
}
&::after {
border-block-end: 1px dotted var(--color-subtle-dark);
content: "";
@@ -442,9 +452,12 @@
}
.bubble__shape {
background-color: var(--bubble-color);
background-color: var(--bubble-background-color, var(--bubble-color));
block-size: var(--bubble-size, 1.2em);
border-color: var(--bubble-border-color, var(--bubble-color));
border-radius: var(--bubble-shape);
border-style: var(--bubble-border-style, solid);
border-width: var(--bubble-border-width, 0);
inline-size: var(--bubble-size, 1.2em);
pointer-events: none;
position: relative;
@@ -454,13 +467,17 @@
.bubble & {
--bubble-size: 100%;
background-color: color(from var(--bubble-color) srgb r g b / 0.15);
border: var(--bubble-border-width) solid var(--bubble-color);
--bubble-background-color: color(from var(--bubble-color) srgb r g b / 0.15);
}
.bubble.drafted & {
border-style: dashed;
.drafted & {
--bubble-border-style: dashed;
.bubbles-list & {
--bubble-background-color: color(from var(--bubble-color) srgb r g b / 0.15);
--bubble-border-color: var(--bubble-color);
--bubble-border-width: 0.15rem;
}
}
.bubble:has(.bubble__link) & {
+3 -1
View File
@@ -169,7 +169,9 @@
/* Borders */
.border { border: var(--border-size, 1px) var(--border-style, solid) var(--border-color, var(--color-subtle)); }
.border-top { border-top: var(--border-size, 1px) var(--border-style, solid) var(--border-color, var(--color-subtle)); }
.border-block { border-block: var(--border-size, 1px) var(--border-style, solid) var(--border-color, var(--color-subtle)); }
.border-bottom { border-block-end: var(--border-size, 1px) var(--border-style, solid) var(--border-color, var(--color-subtle)); }
.border-top { border-block-start: var(--border-size, 1px) var(--border-style, solid) var(--border-color, var(--color-subtle)); }
.borderless { border: 0; }
/* Border radius */
@@ -2,10 +2,11 @@ import { Controller } from "@hotwired/stimulus"
const MOVE_ITEM_DATA_TYPE = "x-fizzy/move"
const DIVIDER_ITEM_NODE_NAME = "LI"
const OVERLAP_THRESHOLD = 0.25
export default class extends Controller {
static targets = [ "divider", "dragImage", "count" ]
static classes = [ "installed" ]
static classes = [ "installed", "dragging" ]
static values = { startCount: Number, maxCount: Number }
connect() {
@@ -22,6 +23,7 @@ export default class extends Controller {
event.dataTransfer.dropEffect = "move"
event.dataTransfer.setData(MOVE_ITEM_DATA_TYPE, event.target)
event.dataTransfer.setDragImage(this.dragImageTarget, 0, 0)
this.element.classList.add(this.draggingClass)
}
}
@@ -31,14 +33,22 @@ export default class extends Controller {
}
}
moveDivider({ target }) {
if (target.nodeName == DIVIDER_ITEM_NODE_NAME) {
this.#moveDividerTo(this.#items.indexOf(target))
moveDivider(event) {
if (event.target.nodeName == DIVIDER_ITEM_NODE_NAME) {
const rect = this.dividerTarget.getBoundingClientRect()
const distanceToTop = Math.abs(event.clientY - rect.top)
const distanceToBottom = Math.abs(event.clientY - (rect.top + rect.height))
const distanceToNearestEdge = Math.min(distanceToTop, distanceToBottom)
const overlap = distanceToNearestEdge / rect.height
if (overlap > OVERLAP_THRESHOLD) {
this.#moveDividerTo(this.#items.indexOf(event.target))
}
}
}
persist() {
// TODO
drop() {
this.element.classList.remove(this.draggingClass)
}
#moveDividerTo(index) {
@@ -55,7 +55,7 @@ class AgoFormatter {
}
#pluralize(word, quantity) {
quantity = Math.round(quantity)
quantity = Math.floor(quantity)
const suffix = (quantity === 1) ? "" : "s"
return `${quantity} ${word}${suffix} ago`
}
+1 -2
View File
@@ -1,4 +1,3 @@
<%= button_to bucket_bubble_publish_path(bubble.bucket, bubble), class: "btn btn--positive full-width justify-start borderless" do %>
<%= image_tag "alert.svg", aria: { hidden: true }, size: 24 %>
<%= button_to bucket_bubble_publish_path(bubble.bucket, bubble), class: "btn txt-small btn--link" do %>
<span>Publish</span>
<% end %>
+2 -1
View File
@@ -39,7 +39,8 @@
data-divider-start-count-value="10"
data-divider-max-count-value="10"
data-divider-installed-class="bubbles-list__divider--installed"
data-action="turbo:morph@document->divider#install dragstart->divider#configureDrag dragenter->divider#acceptDrop dragover->divider#acceptDrop dragover->divider#moveDivider drop->divider#persist">
data-divider-dragging-class="dragging"
data-action="turbo:morph@document->divider#install dragstart->divider#configureDrag dragenter->divider#acceptDrop dragover->divider#acceptDrop dragover->divider#moveDivider drop->divider#drop">
<%= render partial: "bubbles/list/bubble", collection: @bubbles, cached: true %>
<%= render "bubbles/list/divider", filter: @filter %>
</ul>
+1 -1
View File
@@ -1,4 +1,4 @@
<li class="flex align-center gap-half margin-none" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %>"
<li class="<%= class_names("flex align-center gap-half margin-none", drafted: bubble.drafted?) %>" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %>"
data-controller="animation" data-animation-play-class="bubble--wobble" data-animation-play-on-load-value="true" data-action="mouseover->animation#play">
<div class="bubble__shape flex-item-no-shrink"></div>
+9 -3
View File
@@ -1,6 +1,14 @@
<% @page_title = @bubble.title %>
<% content_for :header do %>
<% if @bubble.drafted? %>
<div class="flex align-center gap-half fill-selected justify-center border-block margin-block-end"
style="--border-color: var(--color-selected-dark); --border-style: dashed; view-transition-name: draft-banner;">
This a draft, its only visible to you.
<%= render "bubbles/publish", bubble: @bubble %>
</div>
<% end %>
<nav class="align-start">
<%= link_to "#", class: "btn flex-item-justify-start", data: { controller: "hotkey back-navigation", action: "keydown.esc@document->hotkey#click", back_navigation_fallback_destination_value: bubbles_path(bucket_id: @bucket) } do %>
<%= image_tag "arrow-left.svg", aria: { hidden: true }, size: 24 %>
@@ -56,9 +64,7 @@
<% end %>
<hr class="separator--horizontal full-width" style="--border-color: var(--color-subtle);" />
<% if @bubble.drafted? %>
<%= render "bubbles/publish", bubble: @bubble %>
<% else %>
<% unless @bubble.drafted? %>
<%= render "bubbles/pop_toggle", bubble: @bubble %>
<button class="btn full-width justify-start borderless">
<%= image_tag "picture-double.svg", aria: { hidden: true }, size: 24 %>
+1 -1
View File
@@ -3,7 +3,7 @@
<%= link_to bubbles_path(bucket_ids: [ bucket ]), class: "border border-radius margin-block-end-half windshield__container flex justify-center align-center position-relative" do %>
<div class="windshield bucket__windshield flex flex-wrap gap justify-center align-end" style="view-transition-name: windshield_<%= bucket.id %>">
<% bucket.bubbles.ordered_by_activity.limit(10).each do |bubble| %>
<div class="bubble" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %> <%= bubble_size(bubble) %>">
<div class="<%= class_names("bubble", drafted: bubble.drafted?) %>" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %> <%= bubble_size(bubble) %>">
<span class="bubble__shape"></span>
</div>
<% end %>