| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import React from 'react'; |
| import { |
| useActivePlugin, |
| useDocsVersion, |
| useVersions, |
| } from '@docusaurus/plugin-content-docs/client'; |
| import { useLocation } from '@docusaurus/router'; |
| import { useDocsPreferredVersion } from '@docusaurus/theme-common'; |
| import { Dropdown } from 'antd'; |
| import { DownOutlined } from '@ant-design/icons'; |
| |
| import styles from './styles.module.css'; |
| |
| // Map each versioned plugin id to the URL prefix it actually serves |
| // content from. The plugin ids use underscores while several |
| // routeBasePath values use hyphens (and `user_docs` → `/user-docs`), |
| // so without this map the basePath derivation below would mis-split |
| // the pathname for those sections and the version dropdown would |
| // jump to the section root instead of preserving the current page. |
| // |
| // Keep in sync with the `routeBasePath` values in docusaurus.config.ts. |
| const PLUGIN_ID_TO_BASE_PATH = { |
| user_docs: '/user-docs', |
| components: '/components', |
| admin_docs: '/admin-docs', |
| developer_docs: '/developer-docs', |
| }; |
| |
| export default function DocVersionBadge() { |
| const activePlugin = useActivePlugin(); |
| const { pathname } = useLocation(); |
| const pluginId = activePlugin?.pluginId; |
| const [versionedPath, setVersionedPath] = React.useState(''); |
| |
| const isVersioned = pluginId && pluginId in PLUGIN_ID_TO_BASE_PATH; |
| |
| const { preferredVersion } = useDocsPreferredVersion(pluginId); |
| const versions = useVersions(pluginId); |
| const version = useDocsVersion(); |
| |
| // Extract the current page path relative to the version |
| React.useEffect(() => { |
| if (!pathname || !version || !pluginId) return; |
| |
| let relativePath = ''; |
| const basePath = PLUGIN_ID_TO_BASE_PATH[pluginId]; |
| if (!basePath) return; |
| |
| // Handle different version path patterns |
| if (pathname.includes(basePath)) { |
| // Extract the part after the base path |
| const parts = pathname.split(basePath); |
| if (parts.length > 1) { |
| const afterBase = parts[1]; |
| // For versioned paths, remove the version segment |
| if (afterBase.startsWith('/')) { |
| const segments = afterBase.substring(1).split('/'); |
| // Check if first segment is a version (e.g., "1.1.0", "next") |
| if (segments[0] && (segments[0].match(/^\d+\.\d+\.\d+$/) || segments[0] === 'next')) { |
| // Skip the version segment |
| relativePath = segments.length > 1 ? '/' + segments.slice(1).join('/') : ''; |
| } else { |
| // No version in path (e.g., /docs/intro for current version with empty path) |
| relativePath = afterBase; |
| } |
| } |
| } |
| } |
| |
| setVersionedPath(relativePath); |
| }, [pathname, version, pluginId]); |
| |
| // Create dropdown items for version selection |
| const items = versions.map(v => { |
| // Construct the URL for this version, preserving the current page |
| // v.path contains the full path including base, e.g., "/docs/1.1.0" or "/docs" |
| let versionUrl = v.path; |
| |
| if (versionedPath) { |
| // Append the current page path to the version base |
| versionUrl = v.path + versionedPath; |
| } |
| |
| return { |
| key: v.name, |
| label: ( |
| <a href={versionUrl}> |
| {v.label} |
| {v.name === version.name && ' (current)'} |
| {v.name === preferredVersion?.name && ' (preferred)'} |
| </a> |
| ), |
| }; |
| }); |
| |
| if (!isVersioned) { |
| return null; |
| } |
| |
| return ( |
| <span className={styles.versionBadge}> |
| Version:{' '} |
| <Dropdown menu={{ items }} trigger={['click']}> |
| <a onClick={e => e.preventDefault()} className={styles.versionSelector}> |
| {version.label} <DownOutlined /> |
| </a> |
| </Dropdown> |
| </span> |
| ); |
| } |