starlight-sidebar-topics-dropdown 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/config.ts CHANGED
@@ -43,6 +43,11 @@ const sidebarTopicBaseSchema = z.object({
43
43
  const sidebarTopicLinkSchema = sidebarTopicBaseSchema;
44
44
 
45
45
  const sidebarTopicGroupSchema = sidebarTopicBaseSchema.extend({
46
+ /**
47
+ * An optional unique identifier for the topic that can be used to associate content pages that are not listed in any
48
+ * topic sidebar configuration with this topic.
49
+ */
50
+ id: z.string().optional(),
46
51
  /**
47
52
  * The sidebar items (links and subcategories) to display for this topic.
48
53
  *
package/lib/content.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { Props } from "@astrojs/starlight/props";
2
+ import type { TopicFrontmatterSchema } from "../schema";
3
+ export function isStarlightEntryWithTopic(entry: StarlightEntry): entry is StarlightEntryWithTopic {
4
+ return (
5
+ "data" in entry &&
6
+ "topic" in (entry as StarlightEntryWithTopic).data &&
7
+ typeof (entry as StarlightEntryWithTopic).data.topic === "string"
8
+ );
9
+ }
10
+ export type StarlightEntry = Props["entry"];
11
+ export type StarlightEntryWithTopic = StarlightEntry & { data: TopicFrontmatterSchema };
package/lib/sidebar.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Props } from "@astrojs/starlight/props";
2
2
 
3
3
  import type { StarlightSidebarTopicsDropdownSharedConfig } from "./config";
4
+ import { isStarlightEntryWithTopic, type StarlightEntry } from "./content";
4
5
  import { getLocaleFromSlug, getLocalizedSlug } from "./i18n";
5
6
  import { arePathnamesEqual, stripLeadingAndTrailingSlashes } from "./pathname";
6
7
 
@@ -9,8 +10,13 @@ const absoluteLinkRegex = /^https?:\/\//;
9
10
  export function getCurrentTopic(
10
11
  config: StarlightSidebarTopicsDropdownSharedConfig,
11
12
  sidebar: SidebarEntry[],
12
- currentSlug: string
13
+ currentSlug: string,
14
+ entry: StarlightEntry
13
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
+
14
20
  // Start by checking if the current page is a topic root.
15
21
  const topicFromSlug = getTopicFromSlug(config, sidebar, currentSlug);
16
22
  if (topicFromSlug) return topicFromSlug;
@@ -100,6 +106,29 @@ function getTopicFromSlug(
100
106
  return { config: topicConfig, sidebar: topicSidebar };
101
107
  }
102
108
 
109
+ function getTopicById(
110
+ config: StarlightSidebarTopicsDropdownSharedConfig,
111
+ sidebar: SidebarEntry[],
112
+ id: string
113
+ ): Topic | undefined {
114
+ let topicConfig: Topic["config"] | undefined;
115
+ let topicSidebar: Topic["sidebar"] | undefined;
116
+ let groupTopicIndex = -1;
117
+ for (const topic of config) {
118
+ if (topic.type === "group") groupTopicIndex++;
119
+ if (topic.type === "group" && topic.id === id) {
120
+ const sidebarTopic = sidebar[groupTopicIndex];
121
+ if (sidebarTopic?.type === "group") {
122
+ topicConfig = topic;
123
+ topicSidebar = sidebarTopic.entries;
124
+ }
125
+ break;
126
+ }
127
+ }
128
+ if (!topicConfig || !topicSidebar) return;
129
+ return { config: topicConfig, sidebar: topicSidebar };
130
+ }
131
+
103
132
  function getCurrentSidebarTopic(sidebar: SidebarEntry[]): SidebarTopic | undefined {
104
133
  let currentSidebarTopic: SidebarTopic | undefined;
105
134
 
@@ -127,6 +156,10 @@ function getCurrentSidebarEntry(sidebar: SidebarEntry[]): SidebarEntry | undefin
127
156
  });
128
157
  }
129
158
 
159
+ function getTopicIdFromEntry(entry: StarlightEntry): string | undefined {
160
+ return isStarlightEntryWithTopic(entry) ? entry.data.topic : undefined;
161
+ }
162
+
130
163
  type SidebarEntry = Props["sidebar"][number];
131
164
 
132
165
  interface SidebarTopic {
@@ -134,7 +167,7 @@ interface SidebarTopic {
134
167
  entries: SidebarEntry[];
135
168
  }
136
169
 
137
- interface Topic {
170
+ export interface Topic {
138
171
  config: StarlightSidebarTopicsDropdownSharedConfig[number];
139
172
  sidebar: SidebarEntry[];
140
173
  }
@@ -5,15 +5,21 @@ import config from 'virtual:starlight-sidebar-topics-dropdown/config'
5
5
 
6
6
  import Topics from '../components/Topics.astro'
7
7
  import { throwPluginError } from '../lib/plugin'
8
- import { getCurrentTopic } from '../lib/sidebar'
8
+ import { getCurrentTopic, type Topic } from '../lib/sidebar'
9
9
 
10
10
  const { entry, sidebar, slug } = Astro.props
11
11
 
12
12
  let currentSidebar = sidebar
13
- const currentTopic = getCurrentTopic(config, sidebar, slug)
13
+ let currentTopic: Topic | undefined = undefined
14
14
 
15
15
  if (entry['data'].template !== 'splash') {
16
- if (!currentTopic) throwPluginError('Failed to find the topic for the current page.')
16
+ currentTopic = getCurrentTopic(config, sidebar, slug, entry)
17
+ if (!currentTopic)
18
+ throwPluginError(
19
+ 'Failed to find the topic for the current page.',
20
+ `Either include the 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.
21
+ Learn more about unlisted pages in the ["Unlisted pages"](https://starlight-sidebar-topics.netlify.app/docs/guides/unlisted-pages/) guide.`,
22
+ )
17
23
  currentSidebar = currentTopic.sidebar
18
24
  }
19
25
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-sidebar-topics-dropdown",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "description": "Starlight plugin to split your docs page into multiple subpages and switch between them with a dropdown menu in the sidebar.",
6
6
  "author": "trueberryless (https://trueberryless.org)",
@@ -9,6 +9,7 @@
9
9
  ".": "./index.ts",
10
10
  "./overrides/Pagination.astro": "./overrides/Pagination.astro",
11
11
  "./overrides/Sidebar.astro": "./overrides/Sidebar.astro",
12
+ "./schema": "./schema.ts",
12
13
  "./package.json": "./package.json"
13
14
  },
14
15
  "devDependencies": {
@@ -21,6 +22,7 @@
21
22
  "engines": {
22
23
  "node": "^18.17.1 || ^20.3.0 || >=21.0.0"
23
24
  },
25
+ "packageManager": "pnpm@9.3.0",
24
26
  "publishConfig": {
25
27
  "access": "public"
26
28
  },
package/schema.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { z } from "astro/zod";
2
+ export const topicSchema = z.object({
3
+ /**
4
+ * ID of the topic to associate with the current page if the page is not listed in any topic sidebar configuration.
5
+ *
6
+ * @see https://starlight-sidebar-topics.netlify.app/docs/guides/unlisted-pages/
7
+ */
8
+ topic: z.string().optional(),
9
+ });
10
+ export type TopicFrontmatterSchema = z.input<typeof topicSchema>;