@brillout/docpress 0.16.43 → 0.16.44

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.
@@ -12,7 +12,7 @@ function ChoiceGroupContainer({
12
12
  children,
13
13
  choiceGroupAll,
14
14
  }: { children: React.ReactNode; choiceGroupAll: ChoiceGroupWithParent[] }) {
15
- const renderCustomSelect = choiceGroupAll.some((choiceGroup) => choiceGroup.lvl === 0 && !choiceGroup.hidden)
15
+ const renderCustomSelect = (choiceGroupAll ?? []).some((choiceGroup) => choiceGroup.lvl === 0 && !choiceGroup.hidden)
16
16
  return (
17
17
  <div className="choice-group-container">
18
18
  {children}
@@ -49,7 +49,7 @@ function ChoiceGroup({ children, choiceGroup }: { children: React.ReactNode; cho
49
49
  const OPTION_HEIGHT = 25
50
50
  function CustomSelect({ choiceGroup }: { choiceGroup: ChoiceGroupWithParent }) {
51
51
  const radioId = useId()
52
- const choicesAll = usePageContext().config.docpress.choices
52
+ const choicesAll = usePageContext().resolved.choices
53
53
  const { name: groupName, emptyChoices, default: defaultChoice, hidden, parentChoiceGroup, isBuiltIn } = choiceGroup
54
54
  const [selectedChoice, setSelectedChoice] = useCurrentSelection(groupName, defaultChoice)
55
55
  const [expanded, setExpanded] = useState(false)
@@ -57,7 +57,7 @@ function CustomSelect({ choiceGroup }: { choiceGroup: ChoiceGroupWithParent }) {
57
57
  const [parentSelectedChoice] = useCurrentSelection(parentChoiceGroup?.name || '', parentChoiceGroup?.default || '')
58
58
  const setPrevPosition = useRestoreScroll([selectedChoice])
59
59
 
60
- const { choices } = isBuiltIn ? choiceGroup : choicesAll![groupName]!
60
+ const choices = (isBuiltIn ? choiceGroup : choicesAll![groupName]!).choices
61
61
  const isHidden = parentChoiceGroup ? !parentChoiceGroup.choices.includes(parentSelectedChoice) : hidden
62
62
  const isEmptyChoice = (choice: string) => emptyChoices.includes(choice)
63
63
  const filteredChoices = choices.filter((choice) => !isEmptyChoice(choice.name))
@@ -104,7 +104,7 @@ function CustomSelect({ choiceGroup }: { choiceGroup: ChoiceGroupWithParent }) {
104
104
  readOnly
105
105
  />
106
106
  <span className="choice-select__option-content">
107
- <img src={icon} alt="" aria-hidden="true" style={iconStyle} />
107
+ {icon && <img src={icon} alt="" aria-hidden="true" style={iconStyle} />}
108
108
  {choice}
109
109
  </span>
110
110
  </label>
@@ -11,7 +11,7 @@ function Tabs({ choice, hide = [] }: { choice: string; hide: string[] }) {
11
11
  const radioId = useId()
12
12
  const groupName = choice
13
13
  const pageContext = usePageContext()
14
- const choicesAll = pageContext.config.docpress.choices
14
+ const choicesAll = pageContext.resolved.choices
15
15
  assertUsage(choicesAll && choicesAll[groupName], `${groupName} is unknown`)
16
16
  const { choices, default: defaultChoice } = choicesAll[groupName]
17
17
  const [selectedChoice, setSelectedChoice] = useCurrentSelection(groupName, defaultChoice)
@@ -40,7 +40,7 @@ function Tabs({ choice, hide = [] }: { choice: string; hide: string[] }) {
40
40
  }}
41
41
  />
42
42
  <span className="choice-tabs__tab-content">
43
- <img src={icon} alt="" aria-hidden="true" style={iconStyle} />
43
+ {icon && <img src={icon} alt="" aria-hidden="true" style={iconStyle} />}
44
44
  {choice}
45
45
  </span>
46
46
  </label>
@@ -1,9 +1,10 @@
1
1
  export type { ChoiceGroup, ChoiceGroupWithParent, ParentChoiceGroup }
2
2
 
3
- import type { Config } from '../types/Config.js'
3
+ import type { Config, ChoiceItem } from '../types/Config.js'
4
4
 
5
- type ChoiceGroup = NonNullable<Config['choices']>[string] & {
5
+ type ChoiceGroup = Omit<NonNullable<Config['choices']>[string], 'choices'> & {
6
6
  name: string
7
+ choices: ChoiceItem[]
7
8
  emptyChoices: string[]
8
9
  hidden: boolean
9
10
  lvl: number
@@ -8,6 +8,7 @@ import type { MdxJsxAttribute, MdxJsxFlowElement, MdxJsxFlowElementData } from '
8
8
  import { getVikeConfig } from 'vike/plugin'
9
9
  import { assertUsage } from '../../utils/assert.js'
10
10
  import { valueToEstree } from 'estree-util-value-to-estree'
11
+ import { resolveChoices } from './resolveChoices.js'
11
12
 
12
13
  type ChoiceNode = {
13
14
  choiceValue: string
@@ -143,7 +144,7 @@ function resolveChoiceGroupNodes(choiceNodes: ChoiceNode[]) {
143
144
  const vikeConfig = getVikeConfig()
144
145
  const choices = choiceNodes.map((choiceNode) => choiceNode.choiceValue)
145
146
  const { choices: choicesConfig } = vikeConfig.config.docpress
146
- const choicesAll = { ...CHOICES_BUILT_IN, ...choicesConfig }
147
+ const choicesAll = resolveChoices({ ...CHOICES_BUILT_IN, ...choicesConfig })
147
148
 
148
149
  // Resolve to the group that defines ALL of the block's values. Matching a group that merely
149
150
  // shares ANY value would mis-resolve a custom group that collides with a built-in on a single
@@ -153,13 +154,12 @@ function resolveChoiceGroupNodes(choiceNodes: ChoiceNode[]) {
153
154
  )
154
155
  assertUsage(groupName, `Missing group name for [${choices}]. Define it in +docpress.choices.`)
155
156
 
156
- const emptyChoices = choicesAll[groupName]!.choices.filter((choice) => !choices.includes(choice.name)).map(
157
- (choice) => choice.name,
158
- )
157
+ const group = choicesAll[groupName]!
158
+ const emptyChoices = group.choices.filter((choice) => !choices.includes(choice.name)).map((choice) => choice.name)
159
159
 
160
160
  const choiceGroup = {
161
161
  name: groupName,
162
- ...choicesAll[groupName]!,
162
+ ...group,
163
163
  emptyChoices,
164
164
  }
165
165
 
@@ -0,0 +1,15 @@
1
+ export { resolveChoices }
2
+ export type { ResolvedChoices }
3
+
4
+ import type { Choice, ChoiceItem } from '../../types/Config.js'
5
+
6
+ type ResolvedChoices = Record<string, Omit<Choice, 'choices'> & { choices: ChoiceItem[] }>
7
+
8
+ function resolveChoices(choicesConfig: Record<string, Choice>): ResolvedChoices {
9
+ return Object.fromEntries(
10
+ Object.entries(choicesConfig).map(([name, group]) => [name, { ...group, choices: group.choices.map(resolveChoice) }]),
11
+ )
12
+ }
13
+ function resolveChoice(choice: string | ChoiceItem): ChoiceItem {
14
+ return typeof choice === 'string' ? { name: choice } : choice
15
+ }
@@ -9,7 +9,7 @@ function Tabs({ choice, hide = [] }) {
9
9
  const radioId = useId();
10
10
  const groupName = choice;
11
11
  const pageContext = usePageContext();
12
- const choicesAll = pageContext.config.docpress.choices;
12
+ const choicesAll = pageContext.resolved.choices;
13
13
  assertUsage(choicesAll && choicesAll[groupName], `${groupName} is unknown`);
14
14
  const { choices, default: defaultChoice } = choicesAll[groupName];
15
15
  const [selectedChoice, setSelectedChoice] = useCurrentSelection(groupName, defaultChoice);
@@ -22,6 +22,6 @@ function Tabs({ choice, hide = [] }) {
22
22
  setSelectedChoice(choice);
23
23
  } }),
24
24
  React.createElement("span", { className: "choice-tabs__tab-content" },
25
- React.createElement("img", { src: icon, alt: "", "aria-hidden": "true", style: iconStyle }),
25
+ icon && React.createElement("img", { src: icon, alt: "", "aria-hidden": "true", style: iconStyle }),
26
26
  choice)))))));
27
27
  }
@@ -1,7 +1,8 @@
1
1
  export type { ChoiceGroup, ChoiceGroupWithParent, ParentChoiceGroup };
2
- import type { Config } from '../types/Config.js';
3
- type ChoiceGroup = NonNullable<Config['choices']>[string] & {
2
+ import type { Config, ChoiceItem } from '../types/Config.js';
3
+ type ChoiceGroup = Omit<NonNullable<Config['choices']>[string], 'choices'> & {
4
4
  name: string;
5
+ choices: ChoiceItem[];
5
6
  emptyChoices: string[];
6
7
  hidden: boolean;
7
8
  lvl: number;
@@ -2,6 +2,7 @@ export { generateChoiceGroupCode, expressionToAttribute };
2
2
  import { getVikeConfig } from 'vike/plugin';
3
3
  import { assertUsage } from '../../utils/assert.js';
4
4
  import { valueToEstree } from 'estree-util-value-to-estree';
5
+ import { resolveChoices } from './resolveChoices.js';
5
6
  // TODO: determine icon representation for CHOICES_BUILT_IN given lack of SVG/file import support
6
7
  // use SVG URLs for now
7
8
  const CHOICES_BUILT_IN = {
@@ -118,16 +119,17 @@ function resolveChoiceGroupNodes(choiceNodes) {
118
119
  const vikeConfig = getVikeConfig();
119
120
  const choices = choiceNodes.map((choiceNode) => choiceNode.choiceValue);
120
121
  const { choices: choicesConfig } = vikeConfig.config.docpress;
121
- const choicesAll = { ...CHOICES_BUILT_IN, ...choicesConfig };
122
+ const choicesAll = resolveChoices({ ...CHOICES_BUILT_IN, ...choicesConfig });
122
123
  // Resolve to the group that defines ALL of the block's values. Matching a group that merely
123
124
  // shares ANY value would mis-resolve a custom group that collides with a built-in on a single
124
125
  // value — e.g. a `runtime` group [Node, Bun, Deno, Cloudflare] sharing `Bun` with `pkgManager`.
125
126
  const groupName = Object.keys(choicesAll).find((key) => choices.every((choice) => choicesAll[key].choices.some(({ name }) => name === choice)));
126
127
  assertUsage(groupName, `Missing group name for [${choices}]. Define it in +docpress.choices.`);
127
- const emptyChoices = choicesAll[groupName].choices.filter((choice) => !choices.includes(choice.name)).map((choice) => choice.name);
128
+ const group = choicesAll[groupName];
129
+ const emptyChoices = group.choices.filter((choice) => !choices.includes(choice.name)).map((choice) => choice.name);
128
130
  const choiceGroup = {
129
131
  name: groupName,
130
- ...choicesAll[groupName],
132
+ ...group,
131
133
  emptyChoices,
132
134
  };
133
135
  const mergedChoiceNodes = choiceGroup.choices.map((choice) => {
@@ -0,0 +1,7 @@
1
+ export { resolveChoices };
2
+ export type { ResolvedChoices };
3
+ import type { Choice, ChoiceItem } from '../../types/Config.js';
4
+ type ResolvedChoices = Record<string, Omit<Choice, 'choices'> & {
5
+ choices: ChoiceItem[];
6
+ }>;
7
+ declare function resolveChoices(choicesConfig: Record<string, Choice>): ResolvedChoices;
@@ -0,0 +1,7 @@
1
+ export { resolveChoices };
2
+ function resolveChoices(choicesConfig) {
3
+ return Object.fromEntries(Object.entries(choicesConfig).map(([name, group]) => [name, { ...group, choices: group.choices.map(resolveChoice) }]));
4
+ }
5
+ function resolveChoice(choice) {
6
+ return typeof choice === 'string' ? { name: choice } : choice;
7
+ }
@@ -16,6 +16,7 @@ declare function usePageContextLegacy(): {
16
16
  pageTitle: string | null;
17
17
  documentTitle: string;
18
18
  activeCategoryName: string;
19
+ choices: import("../code-blocks/utils/resolveChoices.js").ResolvedChoices | undefined;
19
20
  };
20
21
  declare function usePageContext(): PageContext;
21
22
  declare function PageContextProvider({ pageContext, children, }: {
@@ -16,4 +16,5 @@ declare function resolvePageContext(pageContext: PageContextServer): {
16
16
  pageTitle: string | null;
17
17
  documentTitle: string;
18
18
  activeCategoryName: string;
19
+ choices: import("./code-blocks/utils/resolveChoices.js").ResolvedChoices | undefined;
19
20
  };
@@ -4,6 +4,7 @@ import { jsxToTextContent } from './utils/jsxToTextContent.js';
4
4
  import pc from '@brillout/picocolors';
5
5
  import { parseMarkdownMini } from './parseMarkdownMini.js';
6
6
  import { determineNavItemsColumnLayout } from './determineNavItemsColumnLayout.js';
7
+ import { resolveChoices } from './code-blocks/utils/resolveChoices.js';
7
8
  function resolvePageContext(pageContext) {
8
9
  const config = pageContext.globalContext.config.docpress;
9
10
  const { urlPathname } = pageContext;
@@ -45,6 +46,7 @@ function resolvePageContext(pageContext) {
45
46
  }
46
47
  // Don't show landing page in navigation
47
48
  navItemsAll = navItemsAll.filter((navItem) => navItem.url !== '/');
49
+ const choices = config.choices && resolveChoices(config.choices);
48
50
  const resolved = {
49
51
  navItemsAll,
50
52
  navItemsDetached,
@@ -54,6 +56,7 @@ function resolvePageContext(pageContext) {
54
56
  pageTitle,
55
57
  documentTitle,
56
58
  activeCategoryName,
59
+ choices,
57
60
  };
58
61
  return resolved;
59
62
  }
@@ -1,4 +1,4 @@
1
- export type { Config };
1
+ export type { Config, Choice, ChoiceItem };
2
2
  import type { HeadingDefinition, HeadingDetachedDefinition } from './Heading.js';
3
3
  import type React from 'react';
4
4
  type Config = {
@@ -47,11 +47,13 @@ type Category = string | {
47
47
  /** Hide from Algolia search */
48
48
  hide?: boolean;
49
49
  };
50
+ /** A choice. A plain `string` is shorthand for `{ name: string }` (no icon). */
51
+ type ChoiceItem = {
52
+ name: string;
53
+ icon?: string;
54
+ iconStyle?: React.CSSProperties;
55
+ };
50
56
  type Choice = {
51
- choices: {
52
- name: string;
53
- icon: string;
54
- iconStyle?: React.CSSProperties;
55
- }[];
57
+ choices: (string | ChoiceItem)[];
56
58
  default: string;
57
59
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brillout/docpress",
3
- "version": "0.16.43",
3
+ "version": "0.16.44",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@brillout/picocolors": "^1.0.10",
@@ -18,6 +18,7 @@ import { jsxToTextContent } from './utils/jsxToTextContent.js'
18
18
  import pc from '@brillout/picocolors'
19
19
  import { parseMarkdownMini } from './parseMarkdownMini.js'
20
20
  import { determineNavItemsColumnLayout } from './determineNavItemsColumnLayout.js'
21
+ import { resolveChoices } from './code-blocks/utils/resolveChoices.js'
21
22
 
22
23
  type PageSectionResolved = {
23
24
  url: string | null
@@ -80,6 +81,8 @@ function resolvePageContext(pageContext: PageContextServer) {
80
81
  // Don't show landing page in navigation
81
82
  navItemsAll = navItemsAll.filter((navItem) => navItem.url !== '/')
82
83
 
84
+ const choices = config.choices && resolveChoices(config.choices)
85
+
83
86
  const resolved = {
84
87
  navItemsAll,
85
88
  navItemsDetached,
@@ -89,6 +92,7 @@ function resolvePageContext(pageContext: PageContextServer) {
89
92
  pageTitle,
90
93
  documentTitle,
91
94
  activeCategoryName,
95
+ choices,
92
96
  }
93
97
  return resolved
94
98
  }
package/types/Config.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { Config }
1
+ export type { Config, Choice, ChoiceItem }
2
2
 
3
3
  import type { HeadingDefinition, HeadingDetachedDefinition } from './Heading.js'
4
4
  import type React from 'react'
@@ -62,7 +62,9 @@ type Category =
62
62
  hide?: boolean
63
63
  }
64
64
 
65
+ /** A choice. A plain `string` is shorthand for `{ name: string }` (no icon). */
66
+ type ChoiceItem = { name: string; icon?: string; iconStyle?: React.CSSProperties }
65
67
  type Choice = {
66
- choices: { name: string; icon: string; iconStyle?: React.CSSProperties }[]
68
+ choices: (string | ChoiceItem)[]
67
69
  default: string
68
70
  }