diff --git a/Gemfile b/Gemfile index fbe6dc72f..5c3b0ff65 100644 --- a/Gemfile +++ b/Gemfile @@ -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" diff --git a/app/assets/stylesheets/bubbles.css b/app/assets/stylesheets/bubbles.css index fcd1e2420..864f0d34d 100644 --- a/app/assets/stylesheets/bubbles.css +++ b/app/assets/stylesheets/bubbles.css @@ -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) & { diff --git a/app/assets/stylesheets/utilities.css b/app/assets/stylesheets/utilities.css index 17d5747dd..a512aca62 100644 --- a/app/assets/stylesheets/utilities.css +++ b/app/assets/stylesheets/utilities.css @@ -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 */ diff --git a/app/javascript/controllers/divider_controller.js b/app/javascript/controllers/divider_controller.js index 847563d8f..058bcb6a0 100644 --- a/app/javascript/controllers/divider_controller.js +++ b/app/javascript/controllers/divider_controller.js @@ -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) { diff --git a/app/javascript/controllers/local_time_controller.js b/app/javascript/controllers/local_time_controller.js index 61a750dbf..445545a76 100644 --- a/app/javascript/controllers/local_time_controller.js +++ b/app/javascript/controllers/local_time_controller.js @@ -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` } diff --git a/app/views/bubbles/_publish.html.erb b/app/views/bubbles/_publish.html.erb index 0f7af76c3..1079e1b0b 100644 --- a/app/views/bubbles/_publish.html.erb +++ b/app/views/bubbles/_publish.html.erb @@ -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 %> Publish <% end %> diff --git a/app/views/bubbles/index.html.erb b/app/views/bubbles/index.html.erb index f7255893a..5dff555d4 100644 --- a/app/views/bubbles/index.html.erb +++ b/app/views/bubbles/index.html.erb @@ -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 %> diff --git a/app/views/bubbles/list/_bubble.html.erb b/app/views/bubbles/list/_bubble.html.erb index 68588b64b..8836fd8f2 100644 --- a/app/views/bubbles/list/_bubble.html.erb +++ b/app/views/bubbles/list/_bubble.html.erb @@ -1,4 +1,4 @@ -