Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions docs/app/javascript/controllers/ruby_ui/toaster_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ export default class extends Controller {
dir: { type: String, default: "ltr" },
}

connect() {
initialize() {
this._expanded = false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The docs and gem copies of toaster_controller.js, which the PR states are kept in sync, have drifted: this file reorders the property assignments in initialize() and drops the explanatory comment, while the gem copy still has the old order plus the comment. Behavior is identical, so unless the drift is intentional, mirror one to the other to keep the sync claim accurate and the intent comment preserved.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/app/javascript/controllers/ruby_ui/toaster_controller.js, line 43:

<comment>The docs and gem copies of toaster_controller.js, which the PR states are kept in sync, have drifted: this file reorders the property assignments in initialize() and drops the explanatory comment, while the gem copy still has the old order plus the comment. Behavior is identical, so unless the drift is intentional, mirror one to the other to keep the sync claim accurate and the intent comment preserved.</comment>

<file context>
@@ -40,13 +40,9 @@ export default class extends Controller {
-    // can run safely for toasts already present in the DOM (e.g. server-rendered
-    // flash) before `connect` runs. Stimulus fires `targetConnected` before
-    // `connect` for pre-existing targets, so any state they touch must exist first.
+    this._expanded = false
     this._heights = new Map()
     this._resizeObservers = new WeakMap()
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved — the docs and gem copies are back in sync. The drift you caught was transient: it existed only between the two "Update toaster_controller.js" commits (the first touched only the docs copy). As of e4dc783 both files are byte-for-byte identical (diff -q confirms), with the same initialize() order and no comment. Behavior is unchanged. The MCP registry has also been rebuilt to match the current source.

this._heights = new Map()
this._resizeObservers = new WeakMap()
}

connect() {
this._expanded = this.expandValue
this._listEl = this.element.querySelector("ol") || (this.element.tagName === "OL" ? this.element : null)
this._registerGlobalApi()
Expand All @@ -59,6 +63,8 @@ export default class extends Controller {
this._listEl.addEventListener("pointerenter", this._onPointerEnter)
this._listEl.addEventListener("pointerleave", this._onPointerLeave)
document.addEventListener("keydown", this._onKey)

this._reflow()
}

disconnect() {
Expand All @@ -67,9 +73,21 @@ export default class extends Controller {
this._listEl?.removeEventListener("pointerenter", this._onPointerEnter)
this._listEl?.removeEventListener("pointerleave", this._onPointerLeave)
document.removeEventListener("keydown", this._onKey)
this._listEl = null
}

positionValueChanged(value) {
this.element.setAttribute("data-position", value)
this._reflow()
}

expandValueChanged(value) {
this._expanded = value
this._reflow()
}

toastTargetConnected(el) {
this._resizeObservers.get(el)?.disconnect()
if (typeof ResizeObserver !== "undefined") {
const ro = new ResizeObserver(() => {
this._heights.set(el, el.offsetHeight)
Expand All @@ -90,13 +108,11 @@ export default class extends Controller {
}

_spawn(detail) {
if (!this._listEl) return null
const variant = VARIANTS.includes(detail.variant) ? detail.variant : "default"
const tpl = this._skeletonFor(variant)
if (!tpl) return null
if (detail.position) {
this.element.setAttribute("data-position", detail.position)
this.positionValue = detail.position
}
if (detail.position) this.positionValue = detail.position
const node = tpl.content.firstElementChild.cloneNode(true)

node.id = detail.id || `toast-${this._uuid()}`
Expand Down Expand Up @@ -151,7 +167,7 @@ export default class extends Controller {
)
return
}
const el = this._listEl.querySelector(`#${CSS.escape(id)}`)
const el = this._listEl?.querySelector(`#${CSS.escape(id)}`)
if (el) el.dispatchEvent(new CustomEvent("ruby-ui:toast:force-dismiss", { bubbles: true }))
}

Expand Down Expand Up @@ -243,7 +259,7 @@ export default class extends Controller {
if (wantCtrl !== e.ctrlKey) return
if (wantMeta !== e.metaKey) return
e.preventDefault()
const first = this._listEl.firstElementChild
const first = this._listEl?.firstElementChild
first?.focus()
}

Expand Down Expand Up @@ -278,7 +294,7 @@ export default class extends Controller {
}

_mutate(id, variant, text) {
const el = this._listEl.querySelector(`#${CSS.escape(id)}`)
const el = this._listEl?.querySelector(`#${CSS.escape(id)}`)
if (!el) return
el.dataset.variant = variant
el.setAttribute("role", variant === "error" ? "alert" : "status")
Expand Down
32 changes: 24 additions & 8 deletions gem/lib/ruby_ui/toast/toaster_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ export default class extends Controller {
dir: { type: String, default: "ltr" },
}

connect() {
initialize() {
this._expanded = false
this._heights = new Map()
this._resizeObservers = new WeakMap()
}

connect() {
this._expanded = this.expandValue
this._listEl = this.element.querySelector("ol") || (this.element.tagName === "OL" ? this.element : null)
this._registerGlobalApi()
Expand All @@ -59,6 +63,8 @@ export default class extends Controller {
this._listEl.addEventListener("pointerenter", this._onPointerEnter)
this._listEl.addEventListener("pointerleave", this._onPointerLeave)
document.addEventListener("keydown", this._onKey)

this._reflow()
}

disconnect() {
Expand All @@ -67,9 +73,21 @@ export default class extends Controller {
this._listEl?.removeEventListener("pointerenter", this._onPointerEnter)
this._listEl?.removeEventListener("pointerleave", this._onPointerLeave)
document.removeEventListener("keydown", this._onKey)
this._listEl = null
}

positionValueChanged(value) {
this.element.setAttribute("data-position", value)
this._reflow()
}

expandValueChanged(value) {
this._expanded = value
this._reflow()
}

toastTargetConnected(el) {
this._resizeObservers.get(el)?.disconnect()
if (typeof ResizeObserver !== "undefined") {
const ro = new ResizeObserver(() => {
this._heights.set(el, el.offsetHeight)
Expand All @@ -90,13 +108,11 @@ export default class extends Controller {
}

_spawn(detail) {
if (!this._listEl) return null
const variant = VARIANTS.includes(detail.variant) ? detail.variant : "default"
const tpl = this._skeletonFor(variant)
if (!tpl) return null
if (detail.position) {
this.element.setAttribute("data-position", detail.position)
this.positionValue = detail.position
}
if (detail.position) this.positionValue = detail.position
const node = tpl.content.firstElementChild.cloneNode(true)

node.id = detail.id || `toast-${this._uuid()}`
Expand Down Expand Up @@ -151,7 +167,7 @@ export default class extends Controller {
)
return
}
const el = this._listEl.querySelector(`#${CSS.escape(id)}`)
const el = this._listEl?.querySelector(`#${CSS.escape(id)}`)
if (el) el.dispatchEvent(new CustomEvent("ruby-ui:toast:force-dismiss", { bubbles: true }))
}

Expand Down Expand Up @@ -243,7 +259,7 @@ export default class extends Controller {
if (wantCtrl !== e.ctrlKey) return
if (wantMeta !== e.metaKey) return
e.preventDefault()
const first = this._listEl.firstElementChild
const first = this._listEl?.firstElementChild
first?.focus()
}

Expand Down Expand Up @@ -278,7 +294,7 @@ export default class extends Controller {
}

_mutate(id, variant, text) {
const el = this._listEl.querySelector(`#${CSS.escape(id)}`)
const el = this._listEl?.querySelector(`#${CSS.escape(id)}`)
if (!el) return
el.dataset.variant = variant
el.setAttribute("role", variant === "error" ? "alert" : "status")
Expand Down
Loading