fix(csr): drive tab url restore by announcement, anchor nav submenus
Two mechanisms tried and rejected while testing this against the live
showcase, both failing the same way on a second history step:
- synthesising a click on the matching tab: a re-render replaces the tab
buttons, and clicking a freshly replaced node that has not been bound
does nothing at all
- tracking the last applied value in the runtime: that state drifts out of
step with the component and silently swallows real changes
The runtime is now stateless. It announces the value the URL names via a
wrnexus:tabs:restore event and the component applies it, comparing against
its own selection rather than a DOM attribute a re-render owns.
Nav submenus are anchored so the viewport clamp keeps them on screen.
Comments in the runtime template trimmed to stay inside the size budget
rather than raising the ceiling again.
Known limitation: a second consecutive back/forward does not update the
selection, because the re-render replaces the component root without
rebinding its declarative listeners. That is a framework defect, not a Tabs
one, and needs its own fix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,16 @@ component Tabs {
|
||||
return list.length ? valueOf(list[0], 0) : ""
|
||||
}
|
||||
|
||||
// The selection this instance started with. The runtime falls back to it
|
||||
// when the back button lands on a URL that has no tab parameter at all.
|
||||
shared function defaultValue() {
|
||||
if (active) {
|
||||
return active
|
||||
}
|
||||
var list = itemList()
|
||||
return list.length ? valueOf(list[0], 0) : ""
|
||||
}
|
||||
|
||||
shared function isSelected(item, index) {
|
||||
return valueOf(item, index) === currentValue()
|
||||
}
|
||||
@@ -57,6 +67,34 @@ component Tabs {
|
||||
return orientation === "vertical" ? "vertical" : "horizontal"
|
||||
}
|
||||
|
||||
// Back and forward arrive here. The runtime announces the value the URL
|
||||
// now names; applying it must not write history, or stepping back would
|
||||
// push a new entry and trap the user.
|
||||
client function applyUrlValue(sourceEvent) {
|
||||
var detail = sourceEvent ? sourceEvent.detail : null
|
||||
var value = detail ? detail.value : ""
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
var list = itemList()
|
||||
var found = -1
|
||||
for (var index = 0; index < list.length; index += 1) {
|
||||
if (valueOf(list[index], index) === value) {
|
||||
found = index
|
||||
}
|
||||
}
|
||||
if (found === -1) {
|
||||
return
|
||||
}
|
||||
// Idempotent against our own state rather than a DOM attribute the
|
||||
// re-render owns: popstate can fire for a value already selected.
|
||||
if (value === currentValue()) {
|
||||
return
|
||||
}
|
||||
activeValue = value
|
||||
output.change({ value: value, item: list[found], index: found })
|
||||
}
|
||||
|
||||
client function selectTab(item, index, sourceEvent) {
|
||||
if (item.disabled) {
|
||||
return
|
||||
@@ -64,12 +102,7 @@ component Tabs {
|
||||
var value = valueOf(item, index)
|
||||
activeValue = value
|
||||
|
||||
// A click that came from the back button must not push another entry.
|
||||
var target = sourceEvent ? sourceEvent.currentTarget : null
|
||||
var group = target && target.closest ? target.closest("[data-wrn-tabs-param]") : null
|
||||
var restoring = group && group.getAttribute("data-wrn-tabs-restoring") === "true"
|
||||
|
||||
if (mode === "url" && !restoring && window.history && window.history.pushState) {
|
||||
if (mode === "url" && window.history && window.history.pushState) {
|
||||
var url = new URL(window.location.href)
|
||||
url.searchParams.set(param || "tab", value)
|
||||
window.history.pushState({}, "", url.toString())
|
||||
@@ -91,6 +124,8 @@ component Tabs {
|
||||
data-mode='{mode}'
|
||||
data-param='{param}'
|
||||
data-wrn-tabs-param='{mode === "url" ? param : ""}'
|
||||
data-wrn-tabs-default='{defaultValue()}'
|
||||
@wrnexus:tabs:restore='applyUrlValue(event)'
|
||||
>
|
||||
<div
|
||||
class="wire-tabs__list"
|
||||
|
||||
Reference in New Issue
Block a user