Mirror of
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #40 from trueberryless-org/use-hd-topic

Use HiDeoo's topics plugin

authored by

trueberryless and committed by
GitHub
1aa191fb aabad07d

+170 -1203
+5
.changeset/quiet-bats-wear.md
··· 1 + --- 2 + "starlight-sidebar-topics-dropdown-docs": minor 3 + --- 4 + 5 + Update (and mostly remove) the documentation to match the new "just-a-component" package with the help of [HiDeoo](https://github.com/hideoo).
+63
.changeset/sad-bugs-pump.md
··· 1 + --- 2 + "starlight-sidebar-topics-dropdown": minor 3 + --- 4 + 5 + ⚠️ **BREAKING CHANGE:** This plugin now uses the [Starlight Sidebar Topics](https://starlight-sidebar-topics.netlify.app/) plugin as a peer dependency. Please follow the upgrade guide below to migrate to the new version. 6 + 7 + 1. Install the [Starlight Sidebar Topics](https://starlight-sidebar-topics.netlify.app/) plugin: 8 + 9 + ```sh 10 + npm i starlight-sidebar-topics 11 + ``` 12 + 13 + 2. Update the `starlight-sidebar-topics-dropdown` plugin in your `astro.config.mjs` (use the `starlight-sidebar-topics` plugin instead): 14 + 15 + ```diff lang="js" 16 + // astro.config.mjs 17 + -import starlightSidebarTopicsDropdown from "starlight-sidebar-topics-dropdown"; 18 + +import starlightSidebarTopics from "starlight-sidebar-topics"; 19 + ``` 20 + 21 + 3. Exchange the `starlight-sidebar-topics-dropdown` plugin with the `starlight-sidebar-topics` plugin and add a manual override for the `Sidebar` component where you can use the dropdown component from the `starlight-sidebar-topics-dropdown` plugin: 22 + 23 + ```diff lang="js" 24 + // astro.config.mjs 25 + export default defineConfig({ 26 + integrations: [ 27 + starlight({ 28 + plugins: [ 29 + - starlightSidebarTopicsDropdown([ 30 + + starlightSidebarTopics([ 31 + // Your Starlight Sidebar Topics configuration here (unchanged). 32 + ]), 33 + ], 34 + + components: { 35 + + Sidebar: './src/components/Sidebar.astro', 36 + + }, 37 + }), 38 + ], 39 + }); 40 + ``` 41 + 42 + 4. Create an Astro component to replace the default Starlight `<Sidebar>` component with which will render the topic list dropdown menu and [re-use the default Starlight sidebar](https://starlight.astro.build/guides/overriding-components/#reuse-a-built-in-component): 43 + 44 + ```astro 45 + --- 46 + // src/components/Sidebar.astro 47 + import Default from '@astrojs/starlight/components/Sidebar.astro'; 48 + import TopicsDropdown from 'starlight-sidebar-topics-dropdown/TopicsDropdown.astro'; 49 + --- 50 + 51 + {/* Render the topics dropdown menu. */} 52 + <TopicsDropdown /> 53 + {/* Render the default sidebar. */} 54 + <Default><slot /></Default> 55 + ``` 56 + 57 + 5. Update the schema import in `src/content.config.ts`: 58 + 59 + ```diff lang="ts" 60 + // src/content.config.ts 61 + -import { topicSchema } from "starlight-sidebar-topics-dropdown/schema"; 62 + +import { topicSchema } from "starlight-sidebar-topics/schema"; 63 + ```
+6 -19
docs/astro.config.ts
··· 4 4 import { defineConfig } from "astro/config"; 5 5 import starlightLinksValidator from "starlight-links-validator"; 6 6 import starlightPluginShowLatestVersion from "starlight-plugin-show-latest-version"; 7 - import starlightSidebarTopicsDropdown from "starlight-sidebar-topics-dropdown"; 7 + import starlightSidebarTopics from "starlight-sidebar-topics"; 8 8 9 9 export default defineConfig({ 10 10 integrations: [ ··· 20 20 baseUrl: 21 21 "https://github.com/trueberryless-org/starlight-sidebar-topics-dropdown/edit/main/docs/", 22 22 }, 23 + components: { 24 + Sidebar: "./src/components/Sidebar.astro", 25 + }, 23 26 plugins: [ 24 - starlightSidebarTopicsDropdown([ 27 + starlightSidebarTopics([ 25 28 { 26 29 label: "Documentation", 27 30 link: "/docs/getting-started/", ··· 29 32 items: [ 30 33 { 31 34 label: "Start Here", 32 - items: ["docs/getting-started", "docs/configuration"], 35 + items: ["docs/getting-started"], 33 36 }, 34 - { label: "Guides", autogenerate: { directory: "docs/guides" } }, 35 37 { 36 38 label: "Resources", 37 39 items: [ ··· 78 80 }, 79 81 variant: "caution", 80 82 }, 81 - }, 82 - { 83 - id: "unnested-sidebar", 84 - label: "Unnested Sidebar", 85 - link: "/unnested-sidebar/", 86 - icon: "right-caret", 87 - items: [ 88 - { slug: "unnested-sidebar" }, 89 - { slug: "unnested-sidebar/lorem-ipsum" }, 90 - { 91 - label: "Lorem ipsum", 92 - items: [{ slug: "unnested-sidebar/group/lorem-ipsum" }], 93 - }, 94 - { slug: "unnested-sidebar/lorem-ipsum-2" }, 95 - ], 96 83 }, 97 84 { 98 85 label: "Starlight Docs",
+1
docs/package.json
··· 30 30 "starlight-contributor-list": "^0.2.1", 31 31 "starlight-links-validator": "^0.14.3", 32 32 "starlight-plugin-show-latest-version": "^0.4.0", 33 + "starlight-sidebar-topics": "^0.6.0", 33 34 "starlight-sidebar-topics-dropdown": "workspace:*" 34 35 }, 35 36 "packageManager": "pnpm@9.6.0",
+7
docs/src/components/Sidebar.astro
··· 1 + --- 2 + import Default from "@astrojs/starlight/components/Sidebar.astro"; 3 + import TopicsDropdown from "starlight-sidebar-topics-dropdown/TopicsDropdown.astro"; 4 + --- 5 + 6 + <TopicsDropdown /> 7 + <Default><slot /></Default>
+1 -1
docs/src/content.config.ts
··· 1 1 import { docsLoader } from "@astrojs/starlight/loaders"; 2 2 import { docsSchema } from "@astrojs/starlight/schema"; 3 3 import { defineCollection } from "astro:content"; 4 - import { topicSchema } from "starlight-sidebar-topics-dropdown/schema"; 4 + import { topicSchema } from "starlight-sidebar-topics/schema"; 5 5 6 6 export const collections = { 7 7 docs: defineCollection({
-187
docs/src/content/docs/docs/configuration.mdx
··· 1 - --- 2 - title: Configuration 3 - description: An overview of the configuration options supported by the Starlight Sidebar Topics Dropdown plugin. 4 - --- 5 - 6 - The Starlight Sidebar Topics Dropdown can be configured inside the `astro.config.mjs` configuration file of your project: 7 - 8 - ```js {12} 9 - // astro.config.mjs 10 - // @ts-check 11 - import starlight from "@astrojs/starlight"; 12 - import { defineConfig } from "astro/config"; 13 - import starlightSidebarTopicsDropdown from "starlight-sidebar-topics-dropdown"; 14 - 15 - export default defineConfig({ 16 - integrations: [ 17 - starlight({ 18 - plugins: [ 19 - starlightSidebarTopicsDropdown([ 20 - // Topics configuration goes here. 21 - ]), 22 - ], 23 - title: "My Docs", 24 - }), 25 - ], 26 - }); 27 - ``` 28 - 29 - ## Topic configuration 30 - 31 - The Starlight Sidebar Topics Dropdown plugin accepts an array of topics to display in the sidebar. 32 - 33 - A topic can represent either a section of your documentation with its own sidebar configuration or a direct link to a specific URL, such as an external resource. 34 - 35 - ```js 36 - starlightSidebarTopicsDropdown([ 37 - // A topic representing a guide section of your project. 38 - { 39 - label: "Guides", 40 - icon: "open-book", 41 - // The page to link to when the topic is clicked. 42 - link: "/guides/", 43 - // The sidebar configuration for the topic. 44 - items: [ 45 - { 46 - label: "Start Here", 47 - items: ["guides/concepts", "guides/courses"], 48 - }, 49 - { label: "Recipes", autogenerate: { directory: "guides/recipes" } }, 50 - ], 51 - }, 52 - // A topic redirecting to the Starlight documentation. 53 - { 54 - label: "Starlight docs", 55 - icon: "starlight", 56 - // The URL to the external resource to link to. 57 - link: "https://starlight.astro.build", 58 - }, 59 - ]); 60 - ``` 61 - 62 - A topic can be configured using the following options: 63 - 64 - ### `label` 65 - 66 - **required** 67 - **type:** `string | Record<string, string>` 68 - 69 - The topic label visible at the top of the sidebar. 70 - 71 - The value can be a string, or for multilingual sites, an object with values for each different locale. 72 - When using the object form, the keys must be BCP-47 tags (e.g. `en`, `fr`, or `zh-CN`): 73 - 74 - ```js {3-6} 75 - starlightSidebarTopicsDropdown([ 76 - { 77 - label: { 78 - en: "Starlight documentation", 79 - fr: "Documentation de Starlight", 80 - }, 81 - link: "https://starlight.astro.build", 82 - }, 83 - ]); 84 - ``` 85 - 86 - ### `link` 87 - 88 - **required** 89 - **type:** `string` 90 - 91 - The link to the topic's content which an be a relative link to local files or the full URL of an external page. 92 - 93 - For internal links, the link can either be a page included in the [`items`](#items) array or a different page acting as the topic's landing page. 94 - 95 - ```js {4} 96 - starlightSidebarTopicsDropdown([ 97 - { 98 - label: "Starlight docs", 99 - link: "https://starlight.astro.build", 100 - }, 101 - ]); 102 - ``` 103 - 104 - ### `items` 105 - 106 - **type:** [`SidebarItem[]`](https://starlight.astro.build/reference/configuration/#sidebaritem) 107 - 108 - The topic's sidebar navigation items. 109 - This represents the sidebar displayed when the topic [`link`](#link) page or any of the pages configured in the [`items`](#items) array is the current page. 110 - 111 - This option accepts the same configuration as the [Starlight `sidebar` configuration](https://starlight.astro.build/reference/configuration/#sidebar). 112 - 113 - ```js {5-11} 114 - starlightSidebarTopicsDropdown([ 115 - { 116 - label: "Guides", 117 - link: "/guides/", 118 - items: [ 119 - { 120 - label: "Start Here", 121 - items: ["guides/concepts", "guides/courses"], 122 - }, 123 - { label: "Recipes", autogenerate: { directory: "guides/recipes" } }, 124 - ], 125 - }, 126 - ]); 127 - ``` 128 - 129 - See the [“Sidebar Navigation”](https://starlight.astro.build/guides/sidebar/) guide in the Starlight docs to learn more about sidebar configuration. 130 - 131 - :::note 132 - This plugin adds one special feature to the `items` configuration: the ability to autogenerate sidebar items at the root level of the topic. In other words, the sidebar will only show the topic switcher and a list of links which are not nested under any other item. 133 - 134 - Read more about the [Unnested Sidebar](/docs/guides/unnested-sidebar/) feature. 135 - ::: 136 - 137 - ### `id` 138 - 139 - **type:** string 140 - An optional unique identifier for the topic that can be used to associate content pages that are not listed in any topic sidebar configuration with this topic. 141 - 142 - ```js {5} 143 - StarlightSidebarTopicsDropdown([ 144 - { 145 - label: "Guides", 146 - link: "/guides/", 147 - id: "guides", 148 - items: [{ label: "Start Here", autogenerate: { directory: "guides" } }], 149 - }, 150 - ]); 151 - ``` 152 - 153 - See the [“Unlisted Pages”](/docs/guides/unlisted-pages) guide to learn how to associate content pages with a specific topic. 154 - 155 - ### `icon` 156 - 157 - **type:** string 158 - 159 - The name of an optional icon to display before the topic label set to [one of Starlight's built-in icons](https://starlight.astro.build/reference/icons/#all-icons). 160 - 161 - ```js {5} 162 - starlightSidebarTopicsDropdown([ 163 - { 164 - label: "Starlight docs", 165 - link: "https://starlight.astro.build", 166 - icon: "starlight", 167 - }, 168 - ]); 169 - ``` 170 - 171 - ### `badge` 172 - 173 - **type:** <code>string | [BadgeConfig](https://starlight.astro.build/reference/configuration/#badgeconfig)</code> 174 - 175 - An optional badge to display next to the topic label. 176 - 177 - This option accepts the same configuration as the [Starlight `badge` sidebar item configuration](https://starlight.astro.build/guides/sidebar/#badges). 178 - 179 - ```js {5} 180 - starlightSidebarTopicsDropdown([ 181 - { 182 - label: "Starlight docs", 183 - link: "https://starlight.astro.build", 184 - badge: { text: "Official", variant: "success" }, 185 - }, 186 - ]); 187 - ```
+48 -73
docs/src/content/docs/docs/getting-started.mdx
··· 1 1 --- 2 2 title: Getting Started 3 - description: Learn how to split your documentation into different sections, called topics, each with its own sidebar. 3 + description: Learn how to use a dropdown menu to switch between topics in your Starlight website. 4 4 --- 5 5 6 - An opinionated [Starlight](https://starlight.astro.build) plugin to split your documentation into different sections, called topics, each with its own sidebar. 7 - 8 - Topics represent either a section of your documentation with its own sidebar configuration or a direct link to a specific URL, such as an external resource. 9 - Topics are listed above the default Starlight sidebar links and can be used to navigate between different sections of your documentation. 6 + This package exposes a dropdown menu component meant to be used as a [custom topic list](https://starlight-sidebar-topics.netlify.app/docs/guides/custom-topic-list/) for the [Starlight Sidebar Topics](https://starlight-sidebar-topics.netlify.app/) plugin. 10 7 11 8 :::tip[See it in action] 12 - At the top of the sidebar, you can find links to different topics used in this documentation, such as "Documentation" and "Demo" that each have their own sidebar configuration, while the "Starlight Docs" topic is a direct link to the Starlight documentation. 13 - ::: 14 - 15 - :::caution 16 - This plugin originates from [HiDeoo](https://github.com/HiDeoo) but is not affiliated with him. Nonetheless, huge thanks to him for his work on this plugin. We just changed the topic switcher component, the rest is the same. Make sure to check out [the original plugin](https://github.com/HiDeoo/starlight-sidebar-topics). 9 + At the top of the sidebar, you can find the dropdown menu in action to switch between the different topics used in this documentation, such as "Documentation" and "Demo" that each have their own sidebar configuration, while the "Starlight Docs" topic is a direct link to the Starlight documentation. 17 10 ::: 18 11 19 12 ## Prerequisites 20 13 21 14 You will need to have a Starlight website set up. 22 15 If you don't have one yet, you can follow the ["Getting Started"](https://starlight.astro.build/getting-started) guide in the Starlight docs to create one. 16 + 17 + You will also need to have the [Starlight Sidebar Topics](https://starlight-sidebar-topics.netlify.app/) plugin installed and configured. 18 + If you don't have it set up yet, you can follow the ["Getting Started"](https://starlight-sidebar-topics.netlify.app/getting-started) guide in the Starlight Sidebar Topics docs. 23 19 24 20 ## Installation 25 21 ··· 28 24 29 25 <Steps> 30 26 31 - 1. Starlight Sidebar Topics Dropdown is a Starlight [plugin](https://starlight.astro.build/reference/plugins/). Install it using your favorite package manager: 27 + 1. Starlight Sidebar Topics Dropdown is a Starlight component. Install it using your favorite package manager: 32 28 33 - <PackageManagers pkg="starlight-sidebar-topics-dropdown" /> 29 + <PackageManagers pkg="starlight-sidebar-topics-dropdown" /> 34 30 35 - 2. Configure the plugin in your Starlight [configuration](https://starlight.astro.build/reference/configuration/#plugins) in the `astro.config.mjs` file by specifying an array of topics each with its own sidebar configuration. 31 + 2. To replace the topic list built-in with the Starlight Sidebar Topics plugin by the dropdown menu provided by this package, configure a [component override](https://starlight.astro.build/guides/overriding-components/#how-to-override) in the `astro.config.mjs` file for the `<Sidebar>` component: 36 32 37 - The following example shows how to configure two new topics named "Guides" and "Reference" linking to the `/guides/` and `/reference/` pages respectively with their own sidebar configuration: 33 + ```diff lang="js" 34 + // astro.config.mjs 35 + // @ts-check 36 + import starlight from '@astrojs/starlight' 37 + import { defineConfig } from 'astro/config' 38 + import starlightSidebarTopics from 'starlight-sidebar-topics' 38 39 39 - ```diff lang="js" 40 - // astro.config.mjs 41 - // @ts-check 42 - import starlight from '@astrojs/starlight' 43 - import { defineConfig } from 'astro/config' 44 - +import starlightSidebarTopicsDropdown from 'starlight-sidebar-topics-dropdown' 40 + export default defineConfig({ 41 + integrations: [ 42 + starlight({ 43 + plugins: [ 44 + starlightSidebarTopics([ 45 + // Your Starlight Sidebar Topics configuration here. 46 + ]), 47 + ], 48 + + components: { 49 + + // Override the default `Sidebar` component with a custom one. 50 + + Sidebar: './src/components/Sidebar.astro', 51 + + }, 52 + title: 'My Docs', 53 + }), 54 + ], 55 + }) 56 + ``` 45 57 46 - export default defineConfig({ 47 - integrations: [ 48 - starlight({ 49 - + plugins: [ 50 - + starlightSidebarTopicsDropdown([ 51 - + { 52 - + label: 'Guides', 53 - + link: '/guides/', 54 - + icon: 'open-book', 55 - + items: ['guides/getting-started', 'guides/manual-setup'], 56 - + }, 57 - + { 58 - + label: 'Reference', 59 - + link: '/reference/', 60 - + icon: 'information', 61 - + items: ['reference/api', 'reference/components'], 62 - + }, 63 - + ]), 64 - + ], 65 - - sidebar: [ 66 - - { 67 - - label: 'Guides', 68 - - items: ['guides/getting-started', 'guides/manual-setup'], 69 - - }, 70 - - { 71 - - label: 'Reference', 72 - - items: ['reference/api', 'reference/components'], 73 - - }, 74 - - ], 75 - title: 'My Docs', 76 - }), 77 - ], 78 - }) 79 - ``` 80 - 81 - 3. [Start the development server](https://starlight.astro.build/getting-started/#start-the-development-server) to preview the changes. 82 - 83 - </Steps> 58 + 3. Create an Astro component to replace the default Starlight `<Sidebar>` component with which will render the topic list dropdown menu and [re-use the default Starlight sidebar](https://starlight.astro.build/guides/overriding-components/#reuse-a-built-in-component): 84 59 85 - The Starlight Sidebar Topics Dropdown plugin behavior can be tweaked using various [topic configuration options](/docs/configuration/#topic-configuration). 60 + ```astro 61 + --- 62 + // src/components/Sidebar.astro 63 + import Default from '@astrojs/starlight/components/Sidebar.astro'; 64 + import TopicsDropdown from 'starlight-sidebar-topics-dropdown/TopicsDropdown.astro'; 65 + --- 86 66 87 - ## Component overrides 88 - 89 - The Starlight Sidebar Topics Dropdown plugin uses a Starlight [component override](https://starlight.astro.build/guides/overriding-components/) for the [`Sidebar`](https://starlight.astro.build/reference/overrides/#sidebar) component to add the dropdown which enables choosing between different topics. 67 + {/* Render the topics dropdown menu. */} 68 + <TopicsDropdown /> 69 + {/* Render the default sidebar. */} 70 + <Default><slot /></Default> 71 + ``` 90 72 91 - If you have a custom `Sidebar` component override in your Starlight project, you will need to manually render the `Sidebar` component from the Starlight Sidebar Topics Dropdown plugin in your custom component: 73 + 4. [Start the development server](https://starlight.astro.build/getting-started/#start-the-development-server) to preview the changes. 92 74 93 - ```diff lang="astro" 94 - --- 95 - // src/components/overrides/Sidebar.astro 96 - import Default from '@astrojs/starlight/components/Sidebar.astro' 97 - +import StarlightSidebarTopicsDropdownSidebar from 'starlight-sidebar-topics-dropdown/components/Sidebar.astro' 98 - --- 75 + </Steps> 99 76 100 - +<StarlightSidebarTopicsDropdownSidebar /> 101 - <p>Custom content in the Sidebar override</p> 102 - <Default><slot /></Default> 103 - ``` 77 + You can also render the dropdown menu in a different location in your website by overriding different components. 78 + Lean more about [overriding components](https://starlight.astro.build/guides/overriding-components/) in the Starlight docs.
-101
docs/src/content/docs/docs/guides/unlisted-pages.mdx
··· 1 - --- 2 - title: Unlisted Pages 3 - description: Learn how to associate content pages that are not listed in any topic sidebar configuration with a specific topic. 4 - --- 5 - 6 - By default, the Starlight Sidebar Topics Dropdown plugin expect that every [content pages](https://starlight.astro.build/guides/pages/#content-pages) in your project is associated with a topic. 7 - This is done by including the content page in a [topic sidebar configuration](/docs/configuration#items) and the plugin will automatically determine which sidebar to display based on the current page. 8 - 9 - However, there are cases where you may want to have a content page that is not listed in any topic sidebar while still displaying the sidebar of a specific topic. 10 - 11 - :::note 12 - As an example for such a page, you can visit [the secret demo page](/demo/secret), which is not displayed in the sidebar, but is associated with the “Demo” topic through the `topic: demo` frontmatter entry. 13 - ::: 14 - 15 - ## Configure content collections 16 - 17 - Starlight is built on top of Astro's [content collections](https://docs.astro.build/en/guides/content-collections/), which are configured in the `src/content.config.ts` file. 18 - 19 - To add support for unlisted content pages, update the content config file to add support for associating content pages with a specific topic: 20 - 21 - ```diff lang=ts ins="{ extend: topicSchema }" 22 - // src/content.config.ts 23 - import { defineCollection } from 'astro:content' 24 - import { docsLoader } from '@astrojs/starlight/loaders' 25 - import { docsSchema } from '@astrojs/starlight/schema' 26 - +import { topicSchema } from 'starlight-sidebar-topics-dropdown/schema' 27 - 28 - export const collections = { 29 - docs: defineCollection({ 30 - loader: docsLoader(), 31 - schema: docsSchema({ extend: topicSchema }) 32 - }), 33 - } 34 - ``` 35 - 36 - ## Create a topic ID 37 - 38 - To associate an unlisted content page with a specific topic, the topic must define an [`id`](/docs/configuration#id) in its [configuration](/docs/configuration#topic-configuration): 39 - 40 - ```js {16-17} 41 - // astro.config.mjs 42 - // @ts-check 43 - import starlight from "@astrojs/starlight"; 44 - import { defineConfig } from "astro/config"; 45 - import StarlightSidebarTopicsDropdown from "starlight-sidebar-topics-dropdown"; 46 - 47 - export default defineConfig({ 48 - integrations: [ 49 - starlight({ 50 - plugins: [ 51 - StarlightSidebarTopicsDropdown([ 52 - { 53 - label: "Guides", 54 - icon: "open-book", 55 - link: "/guides/", 56 - // ID used to associate content pages with this topic. 57 - id: "guides", 58 - items: ["guides/concepts", "guides/courses"], 59 - }, 60 - ]), 61 - ], 62 - title: "My Docs", 63 - }), 64 - ], 65 - }); 66 - ``` 67 - 68 - ## Associate a page with a topic 69 - 70 - To associate an unlisted content page with a specific topic, you can use the `topic` [frontmatter](https://starlight.astro.build/reference/frontmatter/) field in the page's content file: 71 - 72 - ```md {4} 73 - --- 74 - # src/content/docs/guides/support.md 75 - title: Support 76 - topic: guides 77 - --- 78 - 79 - This is the support page. 80 - ``` 81 - 82 - For example, given the following file structure based on this guide: 83 - 84 - import { FileTree } from "@astrojs/starlight/components"; 85 - 86 - <FileTree> 87 - 88 - - src/ 89 - - content/ 90 - - docs/ 91 - - guides/ 92 - - concepts.md 93 - - courses.md 94 - - support.md 95 - 96 - </FileTree> 97 - 98 - Visiting the `guides/concepts`, `guides/courses`, and `guides/support` pages will all display the sidebar of the "Guides" topic. 99 - 100 - - `guides/concepts` and `guides/courses` are explicitly listed in the "Guides" topic sidebar configuration under the [`items`](/docs/configuration#items) key. 101 - - `guides/support` is not listed in the "Guides" topic sidebar configuration but is associated with the "Guides" topic through the `topic: guides` frontmatter entry.
-53
docs/src/content/docs/docs/guides/unnested-sidebar.mdx
··· 1 - --- 2 - title: Unnested Sidebar 3 - description: Learn how to configure the Starlight Sidebar Topics Dropdown plugin to display a single-level sidebar without nesting items. 4 - --- 5 - 6 - By default, the Starlight Sidebar Topics Dropdown plugin displays a nested sidebar with multiple levels of items. This allows you to organize your documentation essentially on four levels: 7 - 8 - 1. **Topics**: Representing the main sections of your documentation. 9 - 2. **Sidebar Groups**: Representing the sub-sections of a topic. 10 - 3. **Sidebar Items / Pages**: Representing the individual pages of a sidebar group. 11 - 4. **Table of Contents**: Representing the headings of a page. 12 - 13 - However, sometimes you don't need so many levels of nesting and want to display a single-level sidebar with all items directly under the topic. This plugin makes this possible by providing the option to configure an unnested sidebar, which will result in only three levels of organization: 14 - 15 - 1. **Topics**: Representing the main sections of your documentation. 16 - 2. **Sidebar Items / Pages**: Representing the individual pages of a topic. 17 - 3. **Table of Contents**: Representing the headings of a page. 18 - 19 - In other words, there are no groups in the sidebar, only the items of a topic are displayed directly under the topic. 20 - 21 - ## Configure an unnested sidebar 22 - 23 - To configure an unnested sidebar, list the items directly inside the [`items` topic array](/docs/configuration#items) using the `slug` option provided by the [`SidebarItem` type](https://starlight.astro.build/reference/configuration/#sidebaritem). Starlight Sidebar Topics Dropdown Plugin automatically applies CSS to make the items look right. 24 - 25 - Unfortunately, there is no option for autogenerating an unnested sidebar, which is currently a limitation by Starlight. 26 - 27 - Here is an example of this feature: 28 - 29 - ```js "label: \"\"" 30 - export default defineConfig({ 31 - integrations: [ 32 - starlight({ 33 - title: "Starlight Sidebar Topics Dropdown", 34 - plugins: [ 35 - starlightSidebarTopicsDropdown([ 36 - { 37 - id: "unnested-sidebar", 38 - label: "Unnested Sidebar", 39 - link: "/unnested-sidebar/", 40 - icon: "right-caret", 41 - items: [ 42 - { slug: "unnested-sidebar" }, 43 - { slug: "unnested-sidebar/lorem-ipsum" }, 44 - ], 45 - }, 46 - ]), 47 - ], 48 - }), 49 - ], 50 - }); 51 - ``` 52 - 53 - You can see how the sidebar is displayed in the [Unnested Sidebar](/unnested-sidebar/lorem-ipsum/) topic.
-10
docs/src/content/docs/unnested-sidebar/group/lorem-ipsum.md
··· 1 - --- 2 - title: Lorem ispum 3 - --- 4 - 5 - :::note 6 - This page exists solely for demonstration purposes and contains no useful information. 7 - See the Starlight Multiple Sidebar Dropdown plugin [Unnested Sidebar Guide](/docs/guides/unnested-sidebar/) for more information. 8 - ::: 9 - 10 - Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-10
docs/src/content/docs/unnested-sidebar/index.md
··· 1 - --- 2 - title: Index 3 - --- 4 - 5 - :::note 6 - This page exists solely for demonstration purposes and contains no useful information. 7 - See the Starlight Multiple Sidebar Dropdown plugin [Unnested Sidebar Guide](/docs/guides/unnested-sidebar/) for more information. 8 - ::: 9 - 10 - Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-10
docs/src/content/docs/unnested-sidebar/lorem-ipsum-2.md
··· 1 - --- 2 - title: Lorem ispum 2 3 - --- 4 - 5 - :::note 6 - This page exists solely for demonstration purposes and contains no useful information. 7 - See the Starlight Multiple Sidebar Dropdown plugin [Unnested Sidebar Guide](/docs/guides/unnested-sidebar/) for more information. 8 - ::: 9 - 10 - Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-10
docs/src/content/docs/unnested-sidebar/lorem-ipsum.md
··· 1 - --- 2 - title: Lorem ispum 3 - --- 4 - 5 - :::note 6 - This page exists solely for demonstration purposes and contains no useful information. 7 - See the Starlight Multiple Sidebar Dropdown plugin [Unnested Sidebar Guide](/docs/guides/unnested-sidebar/) for more information. 8 - ::: 9 - 10 - Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+17 -83
packages/starlight-sidebar-topics-dropdown/components/Topics.astro packages/starlight-sidebar-topics-dropdown/components/TopicsDropdown.astro
··· 1 1 --- 2 2 import { Badge, Icon } from "@astrojs/starlight/components"; 3 - import { AstroError } from "astro/errors"; 4 3 import type { ComponentProps } from "astro/types"; 5 - import { getRelativeLocaleUrl } from "astro:i18n"; 6 - import starlightConfig from "virtual:starlight/user-config"; 7 - import config from "virtual:starlight-sidebar-topics-dropdown/config"; 8 4 9 - import type { StarlightSidebarTopicsDropdownSharedConfig } from "../libs/config"; 10 - 11 - interface Props { 12 - current: StarlightSidebarTopicsDropdownSharedConfig[number]; 13 - } 14 - 15 - const { current } = Astro.props; 16 - 17 - const defaultLang = 18 - starlightConfig.defaultLocale?.lang || 19 - starlightConfig.defaultLocale?.locale || 20 - "en"; 21 - 22 - function getTranslation( 23 - translations: Record<string, string>, 24 - link: string, 25 - description: string 26 - ) { 27 - const defaultTranslation = translations[defaultLang]; 28 - 29 - if (!defaultTranslation) { 30 - throw new AstroError( 31 - `The ${description} for "${link}" must have a key for the default language "${defaultLang}".`, 32 - "Update the Starlight config to include a topic label for the default language." 33 - ); 34 - } 35 - 36 - let translation = defaultTranslation; 37 - 38 - if (Astro.currentLocale) { 39 - translation = translations[Astro.currentLocale] ?? defaultTranslation; 40 - } 41 - 42 - return translation; 43 - } 5 + const { topics } = Astro.locals.starlightSidebarTopics; 6 + const currentTopic = topics.find((topic) => topic.isCurrent); 44 7 --- 45 8 46 9 <div class="starlight-sidebar-topics-dropdown-dropdown"> ··· 49 12 class="starlight-sidebar-topics-dropdown-button" 50 13 > 51 14 { 52 - current.icon && ( 15 + currentTopic.icon && ( 53 16 <div class="starlight-sidebar-topics-dropdown-button-icon"> 54 - <Icon name={current.icon as ComponentProps<typeof Icon>["name"]} /> 17 + <Icon 18 + name={currentTopic.icon as ComponentProps<typeof Icon>["name"]} 19 + /> 55 20 </div> 56 21 ) 57 22 } 58 23 <span class="starlight-sidebar-topics-dropdown-button-label" 59 - >{ 60 - typeof current.label === "string" 61 - ? current.label 62 - : getTranslation(current.label, current.link, "topic label") 63 - }</span 24 + >{currentTopic.label}</span 64 25 > 65 26 { 66 - current.badge && ( 27 + currentTopic.badge && ( 67 28 <Badge 68 29 class="starlight-sidebar-topics-dropdown-badge" 69 - text={ 70 - typeof current.badge.text === "string" 71 - ? current.badge.text 72 - : getTranslation( 73 - current.badge.text, 74 - current.link, 75 - "current badge text" 76 - ) 77 - } 78 - variant={current.badge.variant} 30 + text={currentTopic.badge.text} 31 + variant={currentTopic.badge.variant} 79 32 /> 80 33 ) 81 34 } ··· 101 54 hidden 102 55 > 103 56 { 104 - config.map((topic) => { 105 - const isLinkTopic = topic.type === "link"; 106 - const isCurrent = isLinkTopic 107 - ? false 108 - : topic.label === current.label && topic.link === current.link; 109 - const link = 110 - !isLinkTopic && Astro.currentLocale 111 - ? getRelativeLocaleUrl(Astro.currentLocale, topic.link) 112 - : topic.link; 113 - const label = 114 - typeof topic.label === "string" 115 - ? topic.label 116 - : getTranslation(topic.label, topic.link, "topic label"); 117 - 57 + topics.map((topic) => { 118 58 return ( 119 59 <a 120 - href={link} 60 + href={topic.link} 121 61 class:list={{ 122 62 "starlight-sidebar-topics-dropdown-item": true, 123 - "starlight-sidebar-topics-dropdown-current-item": isCurrent, 63 + "starlight-sidebar-topics-dropdown-current-item": topic.isCurrent, 124 64 }} 125 65 > 126 66 {topic.icon && ( ··· 130 70 /> 131 71 </div> 132 72 )} 133 - <span class="starlight-sidebar-topics-dropdown-label">{label}</span> 73 + <span class="starlight-sidebar-topics-dropdown-label"> 74 + {topic.label} 75 + </span> 134 76 {topic.badge && ( 135 77 <Badge 136 78 class="starlight-sidebar-topics-dropdown-badge" 137 - text={ 138 - typeof topic.badge.text === "string" 139 - ? topic.badge.text 140 - : getTranslation( 141 - topic.badge.text, 142 - topic.link, 143 - "topic badge text" 144 - ) 145 - } 79 + text={topic.badge.text} 146 80 variant={topic.badge.variant} 147 81 /> 148 82 )}
-37
packages/starlight-sidebar-topics-dropdown/components/starlight/Sidebar.astro
··· 1 - --- 2 - import { 3 - StarlightSidebarTopicsDropdownLocalsSymbol, 4 - type StarlightSidebarTopicsDropdownLocals, 5 - } from "../../libs/locals"; 6 - import Topics from "../Topics.astro"; 7 - const { entry } = Astro.locals.starlightRoute; 8 - // @ts-expect-error - See `libs/locals` for more information. 9 - const currentTopic = Astro.locals[ 10 - StarlightSidebarTopicsDropdownLocalsSymbol 11 - ] as StarlightSidebarTopicsDropdownLocals; 12 - --- 13 - 14 - { 15 - entry["data"].template !== "splash" && currentTopic && ( 16 - <Topics current={currentTopic.config} /> 17 - ) 18 - } 19 - 20 - <style is:global> 21 - sl-sidebar-state-persist ul.top-level > li:not(:has(details)) { 22 - margin-top: 0rem; 23 - } 24 - sl-sidebar-state-persist ul.top-level > li:has(details) { 25 - margin-block: 0.5rem; 26 - } 27 - /* sl-sidebar-state-persist ul.top-level > li > a { 28 - font-size: var(--sl-text-sm); 29 - } */ 30 - sl-sidebar-state-persist ul.top-level > li > a[aria-current="page"] { 31 - font-weight: 600; 32 - } 33 - sl-sidebar-state-persist ul.top-level > li > a[aria-current="false"] { 34 - font-weight: 400; 35 - color: var(--sl-color-gray-2); 36 - } 37 - </style>
+1 -94
packages/starlight-sidebar-topics-dropdown/index.ts
··· 1 - import type { 2 - StarlightPlugin, 3 - StarlightUserConfig, 4 - } from "@astrojs/starlight/types"; 5 - 6 - import { 7 - StarlightSidebarTopicsDropdownConfigSchema, 8 - type StarlightSidebarTopicsDropdownUserConfig, 9 - } from "./libs/config"; 10 - import { throwPluginError } from "./libs/plugin"; 11 - import { overrideStarlightComponent } from "./libs/starlight"; 12 - import { vitePluginStarlightSidebarTopicsDropdown } from "./libs/vite"; 13 - 14 - export type { 15 - StarlightSidebarTopicsDropdownConfig, 16 - StarlightSidebarTopicsDropdownUserConfig, 17 - } from "./libs/config"; 18 - 19 - export default function starlightSidebarTopicsDropdownPlugin( 20 - userConfig: StarlightSidebarTopicsDropdownUserConfig 21 - ): StarlightPlugin { 22 - const parsedConfig = 23 - StarlightSidebarTopicsDropdownConfigSchema.safeParse(userConfig); 24 - 25 - if (!parsedConfig.success) { 26 - throwPluginError( 27 - `The provided plugin configuration is invalid.\n${parsedConfig.error.issues 28 - .map((issue) => issue.message) 29 - .join("\n")}` 30 - ); 31 - } 32 - 33 - const config = parsedConfig.data; 34 - 35 - return { 36 - name: "starlight-sidebar-topics-dropdown-plugin", 37 - hooks: { 38 - "config:setup"({ 39 - addIntegration, 40 - addRouteMiddleware, 41 - command, 42 - config: starlightConfig, 43 - logger, 44 - updateConfig, 45 - }) { 46 - if (command !== "dev" && command !== "build") return; 47 - 48 - if (starlightConfig.sidebar) { 49 - throwPluginError( 50 - "It looks like you have a `sidebar` configured in your Starlight configuration. To use `starlight-sidebar-topics-dropdown`, create a new topic with your sidebar items.", 51 - "Learn more about topic configuration at https://starlight-sidebar-topics-dropdown.trueberryless.org/docs/configuration/" 52 - ); 53 - } 54 - 55 - addRouteMiddleware({ 56 - entrypoint: "starlight-sidebar-topics-dropdown/middleware", 57 - }); 58 - 59 - const sidebar: StarlightUserConfig["sidebar"] = []; 60 - 61 - for (const [index, topic] of config.entries()) { 62 - if ("items" in topic) { 63 - sidebar.push({ label: String(index), items: topic.items }); 64 - } 65 - } 66 - updateConfig({ 67 - components: { 68 - ...starlightConfig.components, 69 - ...overrideStarlightComponent( 70 - starlightConfig.components, 71 - logger, 72 - "Sidebar", 73 - "Sidebar" 74 - ), 75 - }, 76 - sidebar, 77 - }); 78 - 79 - addIntegration({ 80 - name: "starlight-sidebar-topics-dropdown-integration", 81 - hooks: { 82 - "astro:config:setup": ({ updateConfig }) => { 83 - updateConfig({ 84 - vite: { 85 - plugins: [vitePluginStarlightSidebarTopicsDropdown(config)], 86 - }, 87 - }); 88 - }, 89 - }, 90 - }); 91 - }, 92 - }, 93 - }; 94 - } 1 + export { default as TopicsDropdown } from "./components/TopicsDropdown.astro";
-82
packages/starlight-sidebar-topics-dropdown/libs/config.ts
··· 1 - import type { StarlightUserConfig } from "@astrojs/starlight/types"; 2 - import { z } from "astro/zod"; 3 - 4 - const sidebarTopicBadgeSchema = z.object({ 5 - text: z.union([z.string(), z.record(z.string())]), 6 - variant: z 7 - .enum(["note", "danger", "success", "caution", "tip", "default"]) 8 - .default("default"), 9 - }); 10 - 11 - const sidebarTopicBaseSchema = z.object({ 12 - /** 13 - * An optional badge to display next to the topic label. 14 - * 15 - * This option accepts the same configuration as the Starlight badge sidebar item configuration. 16 - * @see https://starlight.astro.build/guides/sidebar/#badges 17 - */ 18 - badge: z 19 - .union([z.string(), sidebarTopicBadgeSchema]) 20 - .transform((badge) => 21 - typeof badge === "string" 22 - ? { variant: "default" as const, text: badge } 23 - : badge 24 - ) 25 - .optional(), 26 - /** 27 - * The name of an optional icon to display before the topic label set to one of Starlight’s built-in icons. 28 - * @see https://starlight.astro.build/reference/icons/#all-icons 29 - */ 30 - icon: z.string().optional(), 31 - /** 32 - * The topic label visible at the top of the sidebar. 33 - * 34 - * The value can be a string, or for multilingual sites, an object with values for each different locale. When using 35 - * the object form, the keys must be BCP-47 tags (e.g. en, fr, or zh-CN). 36 - */ 37 - label: z.union([z.string(), z.record(z.string())]), 38 - /** 39 - * The link to the topic’s content which an be a relative link to local files or the full URL of an external page. 40 - * 41 - * For internal links, the link can either be a page included in the items array or a different page acting as the 42 - * topic’s landing page. 43 - */ 44 - link: z.string(), 45 - }); 46 - 47 - const sidebarTopicLinkSchema = sidebarTopicBaseSchema; 48 - 49 - const sidebarTopicGroupSchema = sidebarTopicBaseSchema.extend({ 50 - /** 51 - * An optional unique identifier for the topic that can be used to associate content pages that are not listed in any 52 - * topic sidebar configuration with this topic. 53 - */ 54 - id: z.string().optional(), 55 - /** 56 - * The sidebar items (links and subcategories) to display for this topic. 57 - * 58 - * The topic’s sidebar navigation items. This represents the sidebar displayed when the topic `link` page or any of 59 - * the pages configured in the `items` array is the current page. 60 - */ 61 - items: z.any().array() as z.Schema< 62 - NonNullable<StarlightUserConfig["sidebar"]> 63 - >, 64 - }); 65 - 66 - export const StarlightSidebarTopicsDropdownConfigSchema = z 67 - .union([sidebarTopicGroupSchema, sidebarTopicLinkSchema]) 68 - .array(); 69 - 70 - export type StarlightSidebarTopicsDropdownUserConfig = z.input< 71 - typeof StarlightSidebarTopicsDropdownConfigSchema 72 - >; 73 - export type StarlightSidebarTopicsDropdownConfig = z.output< 74 - typeof StarlightSidebarTopicsDropdownConfigSchema 75 - >; 76 - 77 - export type StarlightSidebarTopicsDropdownSharedConfig = ( 78 - | (z.output<typeof sidebarTopicLinkSchema> & { type: "link" }) 79 - | (Omit<z.output<typeof sidebarTopicGroupSchema>, "items"> & { 80 - type: "group"; 81 - }) 82 - )[];
-17
packages/starlight-sidebar-topics-dropdown/libs/content.ts
··· 1 - import type { StarlightRouteData } from "@astrojs/starlight/route-data"; 2 - 3 - import type { TopicFrontmatterSchema } from "../schema"; 4 - 5 - export function isStarlightEntryWithTopic( 6 - entry: StarlightEntry 7 - ): entry is StarlightEntryWithTopic { 8 - return ( 9 - "data" in entry && 10 - "topic" in (entry as StarlightEntryWithTopic).data && 11 - typeof (entry as StarlightEntryWithTopic).data.topic === "string" 12 - ); 13 - } 14 - export type StarlightEntry = StarlightRouteData["entry"]; 15 - export type StarlightEntryWithTopic = StarlightEntry & { 16 - data: TopicFrontmatterSchema; 17 - };
-27
packages/starlight-sidebar-topics-dropdown/libs/i18n.ts
··· 1 - import starlightConfig from "virtual:starlight/user-config"; 2 - 3 - import { stripTrailingSlash } from "./pathname"; 4 - 5 - export function getLocalizedSlug( 6 - slug: string, 7 - locale: string | undefined 8 - ): string { 9 - const slugLocale = getLocaleFromSlug(slug); 10 - if (slugLocale === locale) return slug; 11 - locale ??= ""; 12 - if (slugLocale === slug) return locale; 13 - 14 - if (slugLocale) { 15 - return stripTrailingSlash( 16 - slug.replace(`${slugLocale}/`, locale ? `${locale}/` : "") 17 - ); 18 - } 19 - 20 - return slug ? `${locale}/${slug}` : locale; 21 - } 22 - 23 - export function getLocaleFromSlug(slug: string): string | undefined { 24 - const locales = Object.keys(starlightConfig.locales ?? {}); 25 - const baseSegment = slug.split("/")[0]; 26 - return baseSegment && locales.includes(baseSegment) ? baseSegment : undefined; 27 - }
-14
packages/starlight-sidebar-topics-dropdown/libs/locals.ts
··· 1 - import type { StarlightSidebarTopicsDropdownSharedConfig } from "./config"; 2 - 3 - /** 4 - * We keep track of the current topic configuration using a variable stored using `Astro.locals` which will be reset for 5 - * each new page. The value is tracked using an untyped symbol on purpose to avoid users to get autocomplete for it and 6 - * avoid potential clashes with user-defined variables. 7 - */ 8 - export const StarlightSidebarTopicsDropdownLocalsSymbol = Symbol.for( 9 - "starlight-sidebar-topics-dropdown:locals" 10 - ); 11 - 12 - export interface StarlightSidebarTopicsDropdownLocals { 13 - config: StarlightSidebarTopicsDropdownSharedConfig[number]; 14 - }
-20
packages/starlight-sidebar-topics-dropdown/libs/pathname.ts
··· 1 - export function arePathnamesEqual(pathnameA: string, pathnameB: string) { 2 - return ( 3 - stripLeadingAndTrailingSlashes(pathnameA) === 4 - stripLeadingAndTrailingSlashes(pathnameB) 5 - ); 6 - } 7 - 8 - export function stripLeadingAndTrailingSlashes(pathname: string): string { 9 - return stripLeadingSlash(stripTrailingSlash(pathname)); 10 - } 11 - 12 - export function stripLeadingSlash(pathname: string) { 13 - if (pathname.startsWith("/")) pathname = pathname.slice(1); 14 - return pathname; 15 - } 16 - 17 - export function stripTrailingSlash(pathname: string) { 18 - if (pathname.endsWith("/")) pathname = pathname.slice(0, -1); 19 - return pathname; 20 - }
-9
packages/starlight-sidebar-topics-dropdown/libs/plugin.ts
··· 1 - import { AstroError } from "astro/errors"; 2 - 3 - export function throwPluginError(message: string, hint?: string): never { 4 - throw new AstroError( 5 - message, 6 - hint ?? 7 - `See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/trueberryless-org/starlight-sidebar-topics-dropdown/issues/new/choose` 8 - ); 9 - }
-210
packages/starlight-sidebar-topics-dropdown/libs/sidebar.ts
··· 1 - import type { StarlightRouteData } from "@astrojs/starlight/route-data"; 2 - 3 - import type { StarlightSidebarTopicsDropdownSharedConfig } from "./config"; 4 - import { type StarlightEntry, isStarlightEntryWithTopic } from "./content"; 5 - import { getLocaleFromSlug, getLocalizedSlug } from "./i18n"; 6 - import { arePathnamesEqual, stripLeadingAndTrailingSlashes } from "./pathname"; 7 - 8 - const absoluteLinkRegex = /^https?:\/\//; 9 - 10 - export function getCurrentTopic( 11 - config: StarlightSidebarTopicsDropdownSharedConfig, 12 - sidebar: SidebarEntry[], 13 - currentSlug: string, 14 - entry: StarlightEntry 15 - ): Topic | undefined { 16 - // If the current page has a topic ID, use it to find the topic. 17 - const topicId = getTopicIdFromEntry(entry); 18 - if (topicId) return getTopicById(config, sidebar, topicId); 19 - 20 - // Start by checking if the current page is a topic root. 21 - const topicFromSlug = getTopicFromSlug(config, sidebar, currentSlug); 22 - if (topicFromSlug) return topicFromSlug; 23 - 24 - // Otherwise, find the current topic by looking for the current page in the sidebar. 25 - const currentSidebarTopic = getCurrentSidebarTopic(sidebar); 26 - if (!currentSidebarTopic) return; 27 - 28 - const currentTopicConfig = 29 - config[Number.parseInt(currentSidebarTopic.label, 10)]; 30 - if (!currentTopicConfig) return; 31 - 32 - const sidebarHasGroups = 33 - currentSidebarTopic.entries.length !== 1 || 34 - currentSidebarTopic.entries[0]?.label !== ""; 35 - 36 - return { 37 - config: currentTopicConfig, 38 - sidebar: currentSidebarTopic.entries, 39 - sidebarHasGroups, 40 - }; 41 - } 42 - 43 - export function isTopicFirstPage( 44 - sidebar: SidebarEntry[], 45 - currentSlug: string 46 - ): boolean { 47 - const currentSidebarTopic = getCurrentSidebarTopic(sidebar); 48 - if (!currentSidebarTopic) return false; 49 - 50 - const firstPage = getSidebarFirstPage(currentSidebarTopic.entries); 51 - if (!firstPage) return false; 52 - 53 - return arePathnamesEqual( 54 - stripLeadingAndTrailingSlashes(firstPage.href), 55 - currentSlug 56 - ); 57 - } 58 - 59 - export function isTopicLastPage( 60 - sidebar: SidebarEntry[], 61 - currentSlug: string 62 - ): boolean { 63 - const currentSidebarTopic = getCurrentSidebarTopic(sidebar); 64 - if (!currentSidebarTopic) return false; 65 - 66 - const lastPage = getSidebarLastPage(currentSidebarTopic.entries); 67 - if (!lastPage) return false; 68 - 69 - return arePathnamesEqual( 70 - stripLeadingAndTrailingSlashes(lastPage.href), 71 - currentSlug 72 - ); 73 - } 74 - 75 - function getSidebarFirstPage(sidebar: SidebarEntry[]) { 76 - const entry = sidebar[0]; 77 - if (!entry) return; 78 - if (entry.type === "link") return entry; 79 - 80 - return getSidebarFirstPage(entry.entries); 81 - } 82 - 83 - function getSidebarLastPage(sidebar: SidebarEntry[]) { 84 - const entry = sidebar.at(-1); 85 - if (!entry) return; 86 - if (entry.type === "link") return entry; 87 - 88 - return getSidebarLastPage(entry.entries); 89 - } 90 - 91 - function getTopicFromSlug( 92 - config: StarlightSidebarTopicsDropdownSharedConfig, 93 - sidebar: SidebarEntry[], 94 - slug: string 95 - ): Topic | undefined { 96 - let topicConfig: Topic["config"] | undefined; 97 - let topicSidebar: Topic["sidebar"] | undefined; 98 - let sidebarHasGroups = true; 99 - 100 - // Start by checking if the current page is a topic homepage. 101 - let groupTopicIndex = -1; 102 - const slugLocale = getLocaleFromSlug(slug); 103 - 104 - for (const topic of config) { 105 - if (topic.type === "group") groupTopicIndex++; 106 - 107 - if ( 108 - !absoluteLinkRegex.test(topic.link) && 109 - arePathnamesEqual( 110 - getLocalizedSlug( 111 - stripLeadingAndTrailingSlashes(topic.link), 112 - slugLocale 113 - ), 114 - slug 115 - ) && 116 - groupTopicIndex !== -1 117 - ) { 118 - const sidebarTopic = sidebar[groupTopicIndex]; 119 - 120 - if (sidebarTopic?.type === "group") { 121 - topicConfig = topic; 122 - topicSidebar = sidebarTopic.entries; 123 - sidebarHasGroups = 124 - sidebarTopic.entries.length !== 1 || 125 - sidebarTopic.entries[0]?.label !== ""; 126 - } 127 - 128 - break; 129 - } 130 - } 131 - 132 - if (!topicConfig || !topicSidebar) return; 133 - 134 - return { config: topicConfig, sidebar: topicSidebar, sidebarHasGroups }; 135 - } 136 - 137 - function getTopicById( 138 - config: StarlightSidebarTopicsDropdownSharedConfig, 139 - sidebar: SidebarEntry[], 140 - id: string 141 - ): Topic | undefined { 142 - let topicConfig: Topic["config"] | undefined; 143 - let topicSidebar: Topic["sidebar"] | undefined; 144 - let sidebarHasGroups = true; 145 - let groupTopicIndex = -1; 146 - for (const topic of config) { 147 - if (topic.type === "group") groupTopicIndex++; 148 - if (topic.type === "group" && topic.id === id) { 149 - const sidebarTopic = sidebar[groupTopicIndex]; 150 - if (sidebarTopic?.type === "group") { 151 - topicConfig = topic; 152 - topicSidebar = sidebarTopic.entries; 153 - sidebarHasGroups = 154 - sidebarTopic.entries.length !== 1 || 155 - sidebarTopic.entries[0]?.label !== ""; 156 - } 157 - break; 158 - } 159 - } 160 - if (!topicConfig || !topicSidebar) return; 161 - return { config: topicConfig, sidebar: topicSidebar, sidebarHasGroups }; 162 - } 163 - 164 - function getCurrentSidebarTopic( 165 - sidebar: SidebarEntry[] 166 - ): SidebarTopic | undefined { 167 - let currentSidebarTopic: SidebarTopic | undefined; 168 - 169 - for (const topic of sidebar) { 170 - if (topic.type === "link") continue; 171 - 172 - const currentSidebarEntry = getCurrentSidebarEntry(topic.entries); 173 - 174 - if (currentSidebarEntry) { 175 - currentSidebarTopic = topic; 176 - break; 177 - } 178 - } 179 - 180 - return currentSidebarTopic; 181 - } 182 - 183 - function getCurrentSidebarEntry( 184 - sidebar: SidebarEntry[] 185 - ): SidebarEntry | undefined { 186 - return sidebar.find((entry) => { 187 - if (entry.type === "link") { 188 - return entry.isCurrent; 189 - } 190 - 191 - return getCurrentSidebarEntry(entry.entries); 192 - }); 193 - } 194 - 195 - function getTopicIdFromEntry(entry: StarlightEntry): string | undefined { 196 - return isStarlightEntryWithTopic(entry) ? entry.data.topic : undefined; 197 - } 198 - 199 - type SidebarEntry = StarlightRouteData["sidebar"][number]; 200 - 201 - interface SidebarTopic { 202 - label: string; 203 - entries: SidebarEntry[]; 204 - } 205 - 206 - export interface Topic { 207 - config: StarlightSidebarTopicsDropdownSharedConfig[number]; 208 - sidebar: SidebarEntry[]; 209 - sidebarHasGroups: boolean; 210 - }
-24
packages/starlight-sidebar-topics-dropdown/libs/starlight.ts
··· 1 - import type { StarlightUserConfig } from "@astrojs/starlight/types"; 2 - import type { AstroIntegrationLogger } from "astro"; 3 - 4 - export function overrideStarlightComponent( 5 - components: StarlightUserConfig["components"], 6 - logger: AstroIntegrationLogger, 7 - override: keyof NonNullable<StarlightUserConfig["components"]>, 8 - component: string 9 - ) { 10 - if (components?.[override]) { 11 - logger.warn( 12 - `It looks like you already have a \`${override}\` component override in your Starlight configuration.` 13 - ); 14 - logger.warn( 15 - `To use \`starlight-sidebar-topics-dropdown\`, either remove your override or update it to render the content from \`starlight-sidebar-topics-dropdown/components/${component}.astro\`.` 16 - ); 17 - 18 - return {}; 19 - } 20 - 21 - return { 22 - [override]: `starlight-sidebar-topics-dropdown/overrides/${override}.astro`, 23 - }; 24 - }
-36
packages/starlight-sidebar-topics-dropdown/libs/vite.ts
··· 1 - import type { ViteUserConfig } from "astro"; 2 - 3 - import type { 4 - StarlightSidebarTopicsDropdownConfig, 5 - StarlightSidebarTopicsDropdownSharedConfig, 6 - } from "./config"; 7 - 8 - const moduleId = "virtual:starlight-sidebar-topics-dropdown/config"; 9 - 10 - export function vitePluginStarlightSidebarTopicsDropdown( 11 - config: StarlightSidebarTopicsDropdownConfig 12 - ): VitePlugin { 13 - const resolvedModuleId = `\0${moduleId}`; 14 - 15 - const sharedConfig: StarlightSidebarTopicsDropdownSharedConfig = config.map( 16 - (topic) => { 17 - if (!("items" in topic)) return { ...topic, type: "link" }; 18 - const { items, ...topicWithoutItems } = topic; 19 - return { ...topicWithoutItems, type: "group" }; 20 - } 21 - ); 22 - 23 - const moduleContent = `export default ${JSON.stringify(sharedConfig)}`; 24 - 25 - return { 26 - name: "vite-plugin-starlight-sidebar-topics-dropdown", 27 - load(id) { 28 - return id === resolvedModuleId ? moduleContent : undefined; 29 - }, 30 - resolveId(id) { 31 - return id === moduleId ? resolvedModuleId : undefined; 32 - }, 33 - }; 34 - } 35 - 36 - type VitePlugin = NonNullable<ViteUserConfig["plugins"]>[number];
-43
packages/starlight-sidebar-topics-dropdown/middleware.ts
··· 1 - import { defineRouteMiddleware } from "@astrojs/starlight/route-data"; 2 - import config from "virtual:starlight-sidebar-topics-dropdown/config"; 3 - 4 - import { 5 - type StarlightSidebarTopicsDropdownLocals, 6 - StarlightSidebarTopicsDropdownLocalsSymbol, 7 - } from "./libs/locals"; 8 - import { throwPluginError } from "./libs/plugin"; 9 - import { 10 - getCurrentTopic, 11 - isTopicFirstPage, 12 - isTopicLastPage, 13 - } from "./libs/sidebar"; 14 - 15 - export const onRequest = defineRouteMiddleware((context) => { 16 - const { starlightRoute } = context.locals; 17 - const { entry, id, pagination, sidebar } = starlightRoute; 18 - 19 - if (entry["data"].template !== "splash") { 20 - const currentTopic = getCurrentTopic(config, sidebar, id, entry); 21 - 22 - if (!currentTopic) 23 - throwPluginError( 24 - `Failed to find the topic for the \`${id}\` page.`, 25 - `Either include this page in the sidebar configuration of the desired topic using the \`items\` property or to associate an unlisted page with a topic, use the \`topic\` frontmatter property and set it to the desired topic ID. 26 - Learn more about unlisted pages in the ["Unlisted pages"](https://starlight-sidebar-topics-dropdown.trueberryless.org/docs/guides/unlisted-pages/) guide.` 27 - ); 28 - 29 - starlightRoute.sidebar = currentTopic.sidebar; 30 - // @ts-expect-error - See `libs/locals` for more information. 31 - context.locals[StarlightSidebarTopicsDropdownLocalsSymbol] = { 32 - config: currentTopic.config, 33 - } satisfies StarlightSidebarTopicsDropdownLocals; 34 - 35 - if (isTopicFirstPage(sidebar, id)) { 36 - pagination.prev = undefined; 37 - } 38 - 39 - if (isTopicLastPage(sidebar, id)) { 40 - pagination.next = undefined; 41 - } 42 - } 43 - });
-7
packages/starlight-sidebar-topics-dropdown/overrides/Sidebar.astro
··· 1 - --- 2 - import Default from "@astrojs/starlight/components/Sidebar.astro"; 3 - import StarlightSidebarTopicsDropdownSidebar from "../components/starlight/Sidebar.astro"; 4 - --- 5 - 6 - <StarlightSidebarTopicsDropdownSidebar /> 7 - <Default><slot /></Default>
+3 -5
packages/starlight-sidebar-topics-dropdown/package.json
··· 24 24 "type": "module", 25 25 "exports": { 26 26 ".": "./index.ts", 27 - "./components/Sidebar.astro": "./components/starlight/Sidebar.astro", 28 - "./middleware": "./middleware.ts", 29 - "./overrides/Sidebar.astro": "./overrides/Sidebar.astro", 30 - "./schema": "./schema.ts", 27 + "./TopicsDropdown.astro": "./components/TopicsDropdown.astro", 31 28 "./package.json": "./package.json" 32 29 }, 33 30 "devDependencies": { ··· 35 32 "astro": "^5.3.0" 36 33 }, 37 34 "peerDependencies": { 38 - "@astrojs/starlight": ">=0.30" 35 + "@astrojs/starlight": ">=0.30", 36 + "starlight-sidebar-topics": ">=0.6" 39 37 }, 40 38 "packageManager": "pnpm@9.3.0", 41 39 "engines": {
-11
packages/starlight-sidebar-topics-dropdown/schema.ts
··· 1 - import { z } from "astro/zod"; 2 - 3 - export const topicSchema = z.object({ 4 - /** 5 - * ID of the topic to associate with the current page if the page is not listed in any topic sidebar configuration. 6 - * 7 - * @see https://starlight-sidebar-topics-dropdown.trueberryless.org/docs/guides/unlisted-pages/ 8 - */ 9 - topic: z.string().optional(), 10 - }); 11 - export type TopicFrontmatterSchema = z.input<typeof topicSchema>;
-5
packages/starlight-sidebar-topics-dropdown/starlight.d.ts
··· 1 - declare module "virtual:starlight/user-config" { 2 - const Config: import("@astrojs/starlight/types").StarlightConfig; 3 - 4 - export default Config; 5 - }
-5
packages/starlight-sidebar-topics-dropdown/virtual.d.ts
··· 1 - declare module "virtual:starlight-sidebar-topics-dropdown/config" { 2 - const StarlightSidebarTopicsDropdownConfig: import("./libs/config").StarlightSidebarTopicsDropdownSharedConfig; 3 - 4 - export default StarlightSidebarTopicsDropdownConfig; 5 - }
+18
pnpm-lock.yaml
··· 53 53 starlight-plugin-show-latest-version: 54 54 specifier: ^0.4.0 55 55 version: 0.4.0(@astrojs/starlight@0.32.1(astro@5.3.0(jiti@2.4.2)(rollup@4.34.8)(typescript@5.7.2))) 56 + starlight-sidebar-topics: 57 + specifier: ^0.6.0 58 + version: 0.6.0(@astrojs/starlight@0.32.1(astro@5.3.0(jiti@2.4.2)(rollup@4.34.8)(typescript@5.7.2))) 56 59 starlight-sidebar-topics-dropdown: 57 60 specifier: workspace:* 58 61 version: link:../packages/starlight-sidebar-topics-dropdown 59 62 60 63 packages/starlight-sidebar-topics-dropdown: 64 + dependencies: 65 + starlight-sidebar-topics: 66 + specifier: '>=0.6' 67 + version: 0.6.0(@astrojs/starlight@0.32.1(astro@5.3.0(jiti@2.4.2)(rollup@4.34.8)(typescript@5.7.2))) 61 68 devDependencies: 62 69 '@astrojs/starlight': 63 70 specifier: ^0.32.1 ··· 2147 2154 engines: {node: '>=18'} 2148 2155 peerDependencies: 2149 2156 '@astrojs/starlight': '>=0.30.0' 2157 + 2158 + starlight-sidebar-topics@0.6.0: 2159 + resolution: {integrity: sha512-ysmOR7zaHYKtk18/mpW4MbEMDioR/ZBsisu9bdQrq0v9BlHWpW7gAdWlqFWO9zdv1P7l0Mo1WKd0wJ0UtqOVEQ==} 2160 + engines: {node: '>=18'} 2161 + peerDependencies: 2162 + '@astrojs/starlight': '>=0.32.0' 2150 2163 2151 2164 statuses@2.0.1: 2152 2165 resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} ··· 5244 5257 '@astrojs/starlight': 0.32.1(astro@5.3.0(jiti@2.4.2)(rollup@4.34.8)(typescript@5.7.2)) 5245 5258 transitivePeerDependencies: 5246 5259 - astro 5260 + 5261 + starlight-sidebar-topics@0.6.0(@astrojs/starlight@0.32.1(astro@5.3.0(jiti@2.4.2)(rollup@4.34.8)(typescript@5.7.2))): 5262 + dependencies: 5263 + '@astrojs/starlight': 0.32.1(astro@5.3.0(jiti@2.4.2)(rollup@4.34.8)(typescript@5.7.2)) 5264 + picomatch: 4.0.2 5247 5265 5248 5266 statuses@2.0.1: {} 5249 5267