-
Notifications
You must be signed in to change notification settings - Fork 310
Add region selector for multi-region documentation URLs #2086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
2b5e8ec
Add region selector toggle to docs
claude a2d785b
Fix region selector button styles under Material theme
claude 1e066d4
Make region selector idempotent and instant-nav aware
claude 123a216
Replace page-header region toggle with per-component selectors
claude 6f9f14d
Rename selector label to 'Select Region' and migrate all docs
claude b9e918a
Stop uppercasing the 'Select Region' label
claude 70f9736
Add :robusta-url: inline role and migrate remaining inline-only docs
claude 5eb9a49
Add region picker above the index page Get Started button
claude 8cb8f5a
Turn the inline region selector into a dropdown menu
claude 6168baf
Fix inline dropdown not closing and width misalignment
claude 71c3303
Make inline region dropdown keyboard-operable and harden role parser
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| (function () { | ||
| "use strict"; | ||
|
|
||
| const REGIONS = { | ||
| us: { label: "US", infix: "" }, | ||
| eu: { label: "EU", infix: ".eu" }, | ||
| ap: { label: "AP", infix: ".ap" }, | ||
| }; | ||
| const STORAGE_KEY = "robusta-docs-region"; | ||
| const URL_PATTERN = /\b(platform|api)(?:\.(?:eu|ap))?\.robusta\.dev\b/g; | ||
| const DETECT_PATTERN = /\b(?:platform|api)(?:\.(?:eu|ap))?\.robusta\.dev\b/; | ||
|
|
||
| function getRegion() { | ||
| try { | ||
| const r = localStorage.getItem(STORAGE_KEY); | ||
| return REGIONS[r] ? r : "us"; | ||
| } catch (e) { | ||
| return "us"; | ||
| } | ||
| } | ||
|
|
||
| function saveRegion(r) { | ||
| try { | ||
| localStorage.setItem(STORAGE_KEY, r); | ||
| } catch (e) {} | ||
| } | ||
|
|
||
| function rewrite(text, regionKey) { | ||
| const infix = REGIONS[regionKey].infix; | ||
| return text.replace(URL_PATTERN, function (_, sub) { | ||
| return sub + infix + ".robusta.dev"; | ||
| }); | ||
| } | ||
|
|
||
| const targets = []; | ||
|
|
||
| function resetTargets() { | ||
| targets.length = 0; | ||
| } | ||
|
|
||
| function collectTextNodes(root) { | ||
| const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { | ||
| acceptNode: function (node) { | ||
| if (!node.nodeValue || !DETECT_PATTERN.test(node.nodeValue)) { | ||
| return NodeFilter.FILTER_REJECT; | ||
| } | ||
| const parent = node.parentNode; | ||
| if (parent && (parent.tagName === "SCRIPT" || parent.tagName === "STYLE")) { | ||
| return NodeFilter.FILTER_REJECT; | ||
| } | ||
| return NodeFilter.FILTER_ACCEPT; | ||
| }, | ||
| }); | ||
| let node; | ||
| while ((node = walker.nextNode())) { | ||
| targets.push({ kind: "text", node: node, original: node.nodeValue }); | ||
| } | ||
| } | ||
|
|
||
| function collectAttributes(root) { | ||
| const ATTR_NAMES = ["href", "src", "data-clipboard-text", "value", "title"]; | ||
| const selector = ATTR_NAMES.map(function (a) { return "[" + a + "]"; }).join(","); | ||
| root.querySelectorAll(selector).forEach(function (el) { | ||
| ATTR_NAMES.forEach(function (attr) { | ||
| const v = el.getAttribute(attr); | ||
| if (v && DETECT_PATTERN.test(v)) { | ||
| targets.push({ kind: "attr", node: el, attr: attr, original: v }); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| function applyRegion(regionKey) { | ||
| for (let i = 0; i < targets.length; i++) { | ||
| const t = targets[i]; | ||
| const next = rewrite(t.original, regionKey); | ||
| if (t.kind === "text") { | ||
| if (t.node.nodeValue !== next) t.node.nodeValue = next; | ||
| } else { | ||
| if (t.node.getAttribute(t.attr) !== next) t.node.setAttribute(t.attr, next); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function buildToggle(current) { | ||
| const wrap = document.createElement("div"); | ||
| wrap.className = "robusta-region-selector"; | ||
| wrap.setAttribute("role", "region"); | ||
| wrap.setAttribute("aria-label", "Robusta region selector"); | ||
|
|
||
| const label = document.createElement("span"); | ||
| label.className = "robusta-region-selector__label"; | ||
| label.textContent = "Select your region:"; | ||
| wrap.appendChild(label); | ||
|
|
||
| const group = document.createElement("div"); | ||
| group.className = "robusta-region-selector__options"; | ||
| group.setAttribute("role", "radiogroup"); | ||
|
|
||
| Object.keys(REGIONS).forEach(function (key) { | ||
| const btn = document.createElement("button"); | ||
| btn.type = "button"; | ||
| btn.setAttribute("role", "radio"); | ||
| btn.setAttribute("data-region", key); | ||
| btn.className = "robusta-region-selector__btn"; | ||
| btn.textContent = REGIONS[key].label; | ||
| const active = key === current; | ||
| btn.setAttribute("aria-checked", String(active)); | ||
| if (active) btn.classList.add("is-active"); | ||
| btn.addEventListener("click", function () { | ||
| const r = btn.getAttribute("data-region"); | ||
| saveRegion(r); | ||
| applyRegion(r); | ||
| group.querySelectorAll("button[data-region]").forEach(function (b) { | ||
| const isActive = b.getAttribute("data-region") === r; | ||
| b.classList.toggle("is-active", isActive); | ||
| b.setAttribute("aria-checked", String(isActive)); | ||
| }); | ||
| }); | ||
| group.appendChild(btn); | ||
| }); | ||
|
|
||
| wrap.appendChild(group); | ||
|
|
||
| const note = document.createElement("span"); | ||
| note.className = "robusta-region-selector__note"; | ||
| note.textContent = "URLs on this page will update to match."; | ||
| wrap.appendChild(note); | ||
|
|
||
| return wrap; | ||
| } | ||
|
|
||
| function init() { | ||
| const content = | ||
| document.querySelector(".md-content__inner") || | ||
| document.querySelector("article.md-content__inner") || | ||
| document.querySelector("article") || | ||
| document.querySelector("main"); | ||
| if (!content) return; | ||
|
|
||
| resetTargets(); | ||
|
|
||
| const existing = content.querySelector(".robusta-region-selector"); | ||
| if (existing && existing.parentNode) { | ||
| existing.parentNode.removeChild(existing); | ||
| } | ||
|
|
||
| collectTextNodes(content); | ||
| collectAttributes(content); | ||
| if (targets.length === 0) return; | ||
|
|
||
| const region = getRegion(); | ||
| const toggle = buildToggle(region); | ||
|
|
||
| const firstHeading = content.querySelector("h1"); | ||
| if (firstHeading && firstHeading.parentNode) { | ||
| firstHeading.parentNode.insertBefore(toggle, firstHeading.nextSibling); | ||
| } else { | ||
| content.insertBefore(toggle, content.firstChild); | ||
| } | ||
|
|
||
| applyRegion(region); | ||
| } | ||
|
|
||
| if (document.readyState === "loading") { | ||
| document.addEventListener("DOMContentLoaded", init); | ||
| } else { | ||
| init(); | ||
| } | ||
|
|
||
| // Material/Sphinx-Immaterial instant navigation swaps the content area | ||
| // without firing DOMContentLoaded. The theme exposes an RxJS `document$` | ||
| // observable that emits on every swap; re-run init() on each emission. | ||
| if (typeof window !== "undefined" && window.document$ && typeof window.document$.subscribe === "function") { | ||
| window.document$.subscribe(init); | ||
| } | ||
| })(); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.