From 217f9a0de030c38991626e78a8f18ab9af22ea0f Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Mon, 9 Mar 2026 15:39:41 +0100 Subject: [PATCH 01/11] Remove colored container around card in perma --- app/assets/stylesheets/native.css | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/assets/stylesheets/native.css b/app/assets/stylesheets/native.css index 591ce8890..064db7527 100644 --- a/app/assets/stylesheets/native.css +++ b/app/assets/stylesheets/native.css @@ -59,16 +59,26 @@ /* ------------------------------------------------------------------------ */ .card-perma { - margin-block-start: 0; + margin-block-start: var(--block-space-half); &:not(:has(.card-perma__notch-new-card-buttons)) .card-perma__bg { - padding-block: clamp(0.25rem, 2vw, var(--padding-block)); + padding-block-end: clamp(0.25rem, 2vw, var(--padding-block)); + } + + .card { + background: linear-gradient(to bottom, var(--color-canvas), var(--card-bg-color)); + box-shadow: unset; + } + + .card__board { + border-radius: 0 var(--border-radius) var(--border-radius) 0; } } .card-perma__bg { - border-start-start-radius: calc(0.2em + clamp(0.25rem, 2vw, var(--padding-block))); - border-start-end-radius: calc(0.2em + clamp(0.25rem, 2vw, var(--padding-block))); + padding-inline: 0; + padding-block-start: 0; + background-color: unset; } .card-perma__closure-message { @@ -77,21 +87,10 @@ } .card-perma--draft { - .card-perma__bg { - padding-inline: 0; - padding-block-start: 0; - background-color: unset; - } - .card { - background: linear-gradient(to bottom, var(--color-canvas), var(--card-bg-color)); box-shadow: 0 101vh 0 100vh var(--card-bg-color); } - .card__board { - border-radius: 0 var(--border-radius) var(--border-radius) 0; - } - .card-perma__notch--bottom { z-index: 1; } From a892d52ee09f955562fd0390b7673964adb4d0ba Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Mon, 9 Mar 2026 15:40:30 +0100 Subject: [PATCH 02/11] Move stamp back in the footer by default (narrow viewport) --- app/assets/stylesheets/card-perma.css | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/assets/stylesheets/card-perma.css b/app/assets/stylesheets/card-perma.css index 3dc16d5a2..755dd8f9a 100644 --- a/app/assets/stylesheets/card-perma.css +++ b/app/assets/stylesheets/card-perma.css @@ -34,16 +34,6 @@ } } - @media (max-width: 639px) { - &:has(.card__closed) { - padding-block-start: 4.5rem; - - .card-perma__bg { - box-shadow: 0 -2rem 0 0 var(--color-container); - } - } - } - @media (max-width: 799px) { --half-btn-height: 1.25rem; --padding-inline: 1.5ch; @@ -225,8 +215,7 @@ .card__closed { @media (max-width: 639px) { - inset: auto 0 100% auto; - translate: 0 30%; + inset: auto 0 3rem auto; scale: 75%; } } From 078597f132b0648235d106b055b21f3706e12b13 Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Mon, 9 Mar 2026 15:40:45 +0100 Subject: [PATCH 03/11] Add bridge component --- .../controllers/bridge/stamp_controller.js | 20 +++++++++++++++++++ .../initializers/bridge/bridge_element.js | 16 +++++++++++++++ .../cards/display/common/_stamp.html.erb | 18 +++++++++++++---- 3 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 app/javascript/controllers/bridge/stamp_controller.js diff --git a/app/javascript/controllers/bridge/stamp_controller.js b/app/javascript/controllers/bridge/stamp_controller.js new file mode 100644 index 000000000..42810d49a --- /dev/null +++ b/app/javascript/controllers/bridge/stamp_controller.js @@ -0,0 +1,20 @@ +import { BridgeComponent } from "@hotwired/hotwire-native-bridge" + +export default class extends BridgeComponent { + static component = "stamp" + + connect() { + super.connect() + this.notifyBridgeOfConnect() + } + + disconnect() { + super.disconnect() + this.send("disconnect") + } + + notifyBridgeOfConnect() { + console.log(this.bridgeElement.getStamp()) + this.send("connect", this.bridgeElement.getStamp()) + } +} diff --git a/app/javascript/initializers/bridge/bridge_element.js b/app/javascript/initializers/bridge/bridge_element.js index 438cf22e1..954a8f652 100644 --- a/app/javascript/initializers/bridge/bridge_element.js +++ b/app/javascript/initializers/bridge/bridge_element.js @@ -9,6 +9,14 @@ BridgeElement.prototype.getButton = function() { } } +BridgeElement.prototype.getStamp = function() { + return { + title: this.title, + date: this.getDate(), + by: this.getBy() + } +} + BridgeElement.prototype.getIcon = function() { const url = this.bridgeAttribute(`icon-url`) @@ -19,6 +27,14 @@ BridgeElement.prototype.getIcon = function() { return null } +BridgeElement.prototype.getDate = function() { + return this.bridgeAttribute(`date`) ?? null +} + +BridgeElement.prototype.getBy = function() { + return this.bridgeAttribute(`by`) ?? null +} + BridgeElement.prototype.getDisplayTitle = function() { return !!this.bridgeAttribute(`display-title`) } diff --git a/app/views/cards/display/common/_stamp.html.erb b/app/views/cards/display/common/_stamp.html.erb index 629e4cf24..e8f7e6148 100644 --- a/app/views/cards/display/common/_stamp.html.erb +++ b/app/views/cards/display/common/_stamp.html.erb @@ -1,15 +1,25 @@ <% if card.postponed? %> -
"> + <%= tag.div class: token_list("card__closed", "card__closed--system": card.postponed_by&.system?), data: { + controller: "bridge--stamp", + bridge_title: "Not Now", + bridge_date: card.postponed_at.strftime("%b %d, %Y"), + bridge_by: card.postponed_by&.familiar_name + } do %> Not Now <%= card.postponed_at.strftime("%b %d, %Y") %> -
+ <% end %> <% end %> <% if card.closed? %> -
+ <%= tag.div class: "card__closed", data: { + controller: "bridge--stamp", + bridge_title: "Done", + bridge_date: card.closed_at.strftime("%b %d, %Y"), + bridge_by: card.closed_by.familiar_name + } do %> Done <%= card.closed_at.strftime("%b %d, %Y") %> -
+ <% end %> <% end %> From 38ecacc2b037b46cd8b3eb94186c12fbe939ade3 Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Mon, 9 Mar 2026 15:43:05 +0100 Subject: [PATCH 04/11] Hide web stamp --- app/assets/stylesheets/native.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/native.css b/app/assets/stylesheets/native.css index 064db7527..e636eae14 100644 --- a/app/assets/stylesheets/native.css +++ b/app/assets/stylesheets/native.css @@ -130,3 +130,9 @@ display: none; } } + +[data-bridge-components~=stamp] { + [data-controller~=bridge--stamp] { + display: none; + } +} From d746b86e700c4f41e39533e9f890f32ae99f83fd Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Mon, 9 Mar 2026 15:43:58 +0100 Subject: [PATCH 05/11] Remove debug line --- app/javascript/controllers/bridge/stamp_controller.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/javascript/controllers/bridge/stamp_controller.js b/app/javascript/controllers/bridge/stamp_controller.js index 42810d49a..6c04d3140 100644 --- a/app/javascript/controllers/bridge/stamp_controller.js +++ b/app/javascript/controllers/bridge/stamp_controller.js @@ -14,7 +14,6 @@ export default class extends BridgeComponent { } notifyBridgeOfConnect() { - console.log(this.bridgeElement.getStamp()) this.send("connect", this.bridgeElement.getStamp()) } } From 5a9fd6b975d953f93b6f78c14739258689c52daf Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Mon, 9 Mar 2026 16:38:22 +0100 Subject: [PATCH 06/11] Coding style --- app/javascript/initializers/bridge/bridge_element.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/javascript/initializers/bridge/bridge_element.js b/app/javascript/initializers/bridge/bridge_element.js index 954a8f652..6692a8a22 100644 --- a/app/javascript/initializers/bridge/bridge_element.js +++ b/app/javascript/initializers/bridge/bridge_element.js @@ -18,7 +18,7 @@ BridgeElement.prototype.getStamp = function() { } BridgeElement.prototype.getIcon = function() { - const url = this.bridgeAttribute(`icon-url`) + const url = this.bridgeAttribute("icon-url") if (url) { return { url } @@ -28,17 +28,17 @@ BridgeElement.prototype.getIcon = function() { } BridgeElement.prototype.getDate = function() { - return this.bridgeAttribute(`date`) ?? null + return this.bridgeAttribute("date") ?? null } BridgeElement.prototype.getBy = function() { - return this.bridgeAttribute(`by`) ?? null + return this.bridgeAttribute("by") ?? null } BridgeElement.prototype.getDisplayTitle = function() { - return !!this.bridgeAttribute(`display-title`) + return !!this.bridgeAttribute("display-title") } BridgeElement.prototype.getDisplayAsPrimaryAction = function() { - return !!this.bridgeAttribute(`display-as-primary-action`) + return !!this.bridgeAttribute("display-as-primary-action") } From 7974629e20e0f4721bb3dae11ac66ca90228505f Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Mon, 9 Mar 2026 16:42:59 +0100 Subject: [PATCH 07/11] Rename attribute --- app/javascript/initializers/bridge/bridge_element.js | 6 +++--- app/views/cards/display/common/_stamp.html.erb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/javascript/initializers/bridge/bridge_element.js b/app/javascript/initializers/bridge/bridge_element.js index 6692a8a22..b118c9265 100644 --- a/app/javascript/initializers/bridge/bridge_element.js +++ b/app/javascript/initializers/bridge/bridge_element.js @@ -13,7 +13,7 @@ BridgeElement.prototype.getStamp = function() { return { title: this.title, date: this.getDate(), - by: this.getBy() + closedBy: this.getClosedBy() } } @@ -31,8 +31,8 @@ BridgeElement.prototype.getDate = function() { return this.bridgeAttribute("date") ?? null } -BridgeElement.prototype.getBy = function() { - return this.bridgeAttribute("by") ?? null +BridgeElement.prototype.getClosedBy = function() { + return this.bridgeAttribute("closed-by") ?? null } BridgeElement.prototype.getDisplayTitle = function() { diff --git a/app/views/cards/display/common/_stamp.html.erb b/app/views/cards/display/common/_stamp.html.erb index e8f7e6148..75c11ad4a 100644 --- a/app/views/cards/display/common/_stamp.html.erb +++ b/app/views/cards/display/common/_stamp.html.erb @@ -3,7 +3,7 @@ controller: "bridge--stamp", bridge_title: "Not Now", bridge_date: card.postponed_at.strftime("%b %d, %Y"), - bridge_by: card.postponed_by&.familiar_name + bridge_closed_by: card.postponed_by&.familiar_name } do %> Not Now <%= card.postponed_at.strftime("%b %d, %Y") %> @@ -16,7 +16,7 @@ controller: "bridge--stamp", bridge_title: "Done", bridge_date: card.closed_at.strftime("%b %d, %Y"), - bridge_by: card.closed_by.familiar_name + bridge_closed_by: card.closed_by.familiar_name } do %> Done <%= card.closed_at.strftime("%b %d, %Y") %> From 0b29a67e17e1b8438ae1ae2e37e7f3365c7954f5 Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Tue, 17 Mar 2026 11:09:37 +0100 Subject: [PATCH 08/11] Change date and closedBy properties for a more generic description --- .../controllers/bridge/stamp_controller.js | 10 +++++++++- .../initializers/bridge/bridge_element.js | 16 ---------------- app/views/cards/display/common/_stamp.html.erb | 6 ++---- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/app/javascript/controllers/bridge/stamp_controller.js b/app/javascript/controllers/bridge/stamp_controller.js index 6c04d3140..2be17dc35 100644 --- a/app/javascript/controllers/bridge/stamp_controller.js +++ b/app/javascript/controllers/bridge/stamp_controller.js @@ -14,6 +14,14 @@ export default class extends BridgeComponent { } notifyBridgeOfConnect() { - this.send("connect", this.bridgeElement.getStamp()) + this.send("connect", this.#data) + } + + get #data() { + const bridgeElement = this.bridgeElement + return { + title: bridgeElement.title, + description: bridgeElement.bridgeAttribute("description") ?? null + } } } diff --git a/app/javascript/initializers/bridge/bridge_element.js b/app/javascript/initializers/bridge/bridge_element.js index b118c9265..76f0db8a7 100644 --- a/app/javascript/initializers/bridge/bridge_element.js +++ b/app/javascript/initializers/bridge/bridge_element.js @@ -9,14 +9,6 @@ BridgeElement.prototype.getButton = function() { } } -BridgeElement.prototype.getStamp = function() { - return { - title: this.title, - date: this.getDate(), - closedBy: this.getClosedBy() - } -} - BridgeElement.prototype.getIcon = function() { const url = this.bridgeAttribute("icon-url") @@ -27,14 +19,6 @@ BridgeElement.prototype.getIcon = function() { return null } -BridgeElement.prototype.getDate = function() { - return this.bridgeAttribute("date") ?? null -} - -BridgeElement.prototype.getClosedBy = function() { - return this.bridgeAttribute("closed-by") ?? null -} - BridgeElement.prototype.getDisplayTitle = function() { return !!this.bridgeAttribute("display-title") } diff --git a/app/views/cards/display/common/_stamp.html.erb b/app/views/cards/display/common/_stamp.html.erb index 75c11ad4a..3416fff59 100644 --- a/app/views/cards/display/common/_stamp.html.erb +++ b/app/views/cards/display/common/_stamp.html.erb @@ -2,8 +2,7 @@ <%= tag.div class: token_list("card__closed", "card__closed--system": card.postponed_by&.system?), data: { controller: "bridge--stamp", bridge_title: "Not Now", - bridge_date: card.postponed_at.strftime("%b %d, %Y"), - bridge_closed_by: card.postponed_by&.familiar_name + bridge_description: card.postponed_at.strftime("%b %d, %Y") } do %> Not Now <%= card.postponed_at.strftime("%b %d, %Y") %> @@ -15,8 +14,7 @@ <%= tag.div class: "card__closed", data: { controller: "bridge--stamp", bridge_title: "Done", - bridge_date: card.closed_at.strftime("%b %d, %Y"), - bridge_closed_by: card.closed_by.familiar_name + bridge_description: card.closed_at.strftime("%b %d, %Y") } do %> Done <%= card.closed_at.strftime("%b %d, %Y") %> From 2d1e2019460a6a3c52f995ea0d9c38dc52dec9b4 Mon Sep 17 00:00:00 2001 From: Adrien Maston Date: Tue, 17 Mar 2026 11:57:59 +0100 Subject: [PATCH 09/11] Add scope selector value --- app/javascript/controllers/bridge/stamp_controller.js | 11 +++++------ app/views/cards/display/common/_stamp.html.erb | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/javascript/controllers/bridge/stamp_controller.js b/app/javascript/controllers/bridge/stamp_controller.js index 2be17dc35..735e4c830 100644 --- a/app/javascript/controllers/bridge/stamp_controller.js +++ b/app/javascript/controllers/bridge/stamp_controller.js @@ -2,10 +2,13 @@ import { BridgeComponent } from "@hotwired/hotwire-native-bridge" export default class extends BridgeComponent { static component = "stamp" + static values = { scopeSelector: { type: String, default: "body" } } connect() { - super.connect() - this.notifyBridgeOfConnect() + if (this.element.closest(this.scopeSelectorValue)) { + super.connect() + this.send("connect", this.#data) + } } disconnect() { @@ -13,10 +16,6 @@ export default class extends BridgeComponent { this.send("disconnect") } - notifyBridgeOfConnect() { - this.send("connect", this.#data) - } - get #data() { const bridgeElement = this.bridgeElement return { diff --git a/app/views/cards/display/common/_stamp.html.erb b/app/views/cards/display/common/_stamp.html.erb index 3416fff59..86190e0f6 100644 --- a/app/views/cards/display/common/_stamp.html.erb +++ b/app/views/cards/display/common/_stamp.html.erb @@ -1,6 +1,7 @@ <% if card.postponed? %> <%= tag.div class: token_list("card__closed", "card__closed--system": card.postponed_by&.system?), data: { controller: "bridge--stamp", + bridge__stamp_scope_selector_value: ".card-perma", bridge_title: "Not Now", bridge_description: card.postponed_at.strftime("%b %d, %Y") } do %> @@ -13,6 +14,7 @@ <% if card.closed? %> <%= tag.div class: "card__closed", data: { controller: "bridge--stamp", + bridge__stamp_scope_selector_value: ".card-perma", bridge_title: "Done", bridge_description: card.closed_at.strftime("%b %d, %Y") } do %> From 00b588fcef0370ff01f9e2d80e53d251cb4a30cd Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 17 Mar 2026 07:43:13 -0400 Subject: [PATCH 10/11] Don't display the "Mark done" bridge component button when the card has been marked as "Not Now" --- .../cards/container/_closure_buttons.html.erb | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/app/views/cards/container/_closure_buttons.html.erb b/app/views/cards/container/_closure_buttons.html.erb index 27db6590e..99fcad627 100644 --- a/app/views/cards/container/_closure_buttons.html.erb +++ b/app/views/cards/container/_closure_buttons.html.erb @@ -1,13 +1,27 @@
<%= link_to edit_card_path(card), class: "btn btn--circle-mobile borderless", - data: { controller: "hotkey", action: "keydown.e@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Edit", turbo_frame: dom_id(card, :edit) } do %> + data: { + controller: "hotkey", + action: "keydown.e@document->hotkey#click", + bridge__overflow_menu_target: "item", + bridge_title: "Edit", + turbo_frame: dom_id(card, :edit) + } do %> <%= icon_tag "pencil", class: "icon--mobile-only" %> Edit e <% end %> - <%= button_to card_closure_path(card), class: "btn btn--circle-mobile borderless", - data: { controller: "hotkey", form_target: "submit", bridge__buttons_target: "button", bridge_title: "Mark done", bridge_display_as_primary_action: true, bridge_display_title: true, bridge_icon_url: bridge_icon("check"), action: "keydown.d@document->hotkey#click" }, + <%= button_to card_closure_path(card), class: "btn btn--circle-mobile borderless hide-on-native", + data: { + controller: "hotkey", + form_target: "submit", + bridge__buttons_target: ("button" unless card.postponed?), + bridge_title: "Mark done", + bridge_display_as_primary_action: true, + bridge_display_title: true, bridge_icon_url: bridge_icon("check"), + action: "keydown.d@document->hotkey#click" + }, form: { data: { controller: "form" } } do %> <%= icon_tag "check", class: "icon--mobile-only" %> Mark as Done From 5c1f3a1f4f375eaf4104cc47935dbb578c29f68f Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 17 Mar 2026 08:12:57 -0400 Subject: [PATCH 11/11] Refactor stamp component and observe element changes so subsequent "connect" messages will be sent --- .../controllers/bridge/stamp_controller.js | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/app/javascript/controllers/bridge/stamp_controller.js b/app/javascript/controllers/bridge/stamp_controller.js index 735e4c830..747f5d48d 100644 --- a/app/javascript/controllers/bridge/stamp_controller.js +++ b/app/javascript/controllers/bridge/stamp_controller.js @@ -5,22 +5,40 @@ export default class extends BridgeComponent { static values = { scopeSelector: { type: String, default: "body" } } connect() { + super.connect() + if (this.element.closest(this.scopeSelectorValue)) { - super.connect() - this.send("connect", this.#data) + this.notifyBridgeOfConnect() + this.#observeStamp() } } disconnect() { super.disconnect() + this.notifyBridgeOfDisconnect() + this.stampObserver?.disconnect() + } + + notifyBridgeOfConnect() { + const bridgeElement = this.bridgeElement + + this.send("connect", { + title: bridgeElement.title, + description: bridgeElement.bridgeAttribute("description") + }) + } + + notifyBridgeOfDisconnect() { this.send("disconnect") } - get #data() { - const bridgeElement = this.bridgeElement - return { - title: bridgeElement.title, - description: bridgeElement.bridgeAttribute("description") ?? null - } + #observeStamp() { + this.stampObserver = new MutationObserver(() => { + this.notifyBridgeOfConnect() + }) + + this.stampObserver.observe(this.element, { + attributes: true + }) } }