@brillout/docpress 0.16.30 → 0.16.31

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.
@@ -2,7 +2,7 @@
2
2
  position: relative;
3
3
 
4
4
  &:hover {
5
- .select-container,
5
+ .choice-group__selects,
6
6
  .copy-button {
7
7
  opacity: 1;
8
8
  }
@@ -10,17 +10,23 @@
10
10
 
11
11
  /* layout */
12
12
  --top-position: 10px;
13
- --right-base: 42px;
14
- --right-offset: 0px;
15
13
  --border-color: hsl(0, 0%, 75%) hsl(0, 0%, 72%) hsl(0, 0%, 72%) hsl(0, 0%, 75%);
16
14
 
17
- .select-container {
15
+ .choice-group__selects {
18
16
  position: absolute;
19
- background: #eee;
17
+ display: flex;
18
+ flex-direction: row-reverse;
19
+ gap: 2px;
20
20
  z-index: 3;
21
- font-size: 13.3333px;
22
21
  top: var(--top-position);
23
- right: calc(var(--right-base) + (var(--right-offset)));
22
+ right: 42px;
23
+ opacity: 0;
24
+ transition: opacity 0.5s ease-in-out;
25
+ }
26
+
27
+ .choice-select {
28
+ background: #eee;
29
+ font-size: 13.3333px;
24
30
 
25
31
  -webkit-user-select: none; /* Safari */
26
32
  -moz-user-select: none; /* Firefox */
@@ -30,12 +36,9 @@
30
36
  border-radius: 5px;
31
37
  border-style: solid;
32
38
  border-color: var(--border-color);
33
-
34
- opacity: 0;
35
- transition: opacity 0.5s ease-in-out;
36
39
  }
37
40
 
38
- .sliding-rectangle {
41
+ .choice-select__list {
39
42
  position: relative;
40
43
  border-radius: 5px;
41
44
  border-color: var(--border-color);
@@ -44,7 +47,7 @@
44
47
  transition: top 180ms cubic-bezier(0.2, 0.9, 0.2, 1);
45
48
  }
46
49
 
47
- .select-choice {
50
+ .choice-select__option {
48
51
  display: flex;
49
52
  white-space: nowrap;
50
53
  align-items: center;
@@ -72,21 +75,26 @@
72
75
  transition: left 500ms ease, opacity 150ms ease-in-out;
73
76
  opacity: 0;
74
77
  }
75
- .sliding-rectangle:hover &:hover::after {
78
+
79
+ .choice-select__list:hover &:hover::after {
76
80
  opacity: 1;
77
81
  }
82
+
78
83
  &[aria-selected='true'] {
79
84
  &::after {
80
85
  content: '»' !important;
81
86
  opacity: 1;
82
87
  }
83
- .sliding-rectangle:hover &:not(:hover)::after {
88
+
89
+ .choice-select__list:hover &:not(:hover)::after {
84
90
  opacity: 0;
85
91
  }
92
+
86
93
  &:hover::after {
87
94
  left: var(--animation-width);
88
95
  }
89
96
  }
97
+
90
98
  &[aria-disabled='true'] {
91
99
  &::after {
92
100
  content: '⊘';
@@ -94,44 +102,46 @@
94
102
  left: 1px;
95
103
  }
96
104
  }
105
+
97
106
  &:not([aria-selected='true']):not([aria-disabled='true']) {
98
107
  &::after {
99
108
  content: '«';
100
109
  }
110
+
101
111
  &:hover::after {
102
112
  left: calc(-1 * var(--animation-width));
103
113
  }
104
114
  }
105
115
  }
106
116
 
107
- .select-container[aria-expanded='false'] {
117
+ .choice-select[aria-expanded='false'] {
108
118
  overflow: hidden;
109
119
  border-width: 1px 2px 2px 1px;
110
120
  }
111
121
 
112
- .select-container[aria-expanded='true'] {
122
+ .choice-select[aria-expanded='true'] {
113
123
  overflow: visible;
114
124
  border-width: 0;
115
125
 
116
- .sliding-rectangle {
126
+ .choice-select__list {
117
127
  border-style: solid;
118
128
  border-width: 1px 2px 2px 1px;
119
129
  }
120
130
  }
121
131
 
122
- .select-choice:last-child {
132
+ .choice-select__option:last-child {
123
133
  border-bottom: none;
124
134
  }
125
135
 
126
- .select-choice:hover {
136
+ .choice-select__option:hover {
127
137
  background: #f5f5f5;
128
138
  }
129
139
 
130
- .select-choice[aria-selected='true'] {
140
+ .choice-select__option[aria-selected='true'] {
131
141
  background: #eee;
132
142
  }
133
143
 
134
- .select-choice[aria-disabled='true'] {
144
+ .choice-select__option[aria-disabled='true'] {
135
145
  color: #999;
136
146
  cursor: default;
137
147
  opacity: 0.8;
@@ -1,6 +1,6 @@
1
- export { ChoiceGroup }
1
+ export { ChoiceGroup, CustomSelectsContainer }
2
2
 
3
- import React, { useEffect, useRef, useState } from 'react'
3
+ import React, { createContext, useContext, useEffect, useState } from 'react'
4
4
  import { useSelectedChoice } from '../hooks/useSelectedChoice.js'
5
5
  import { useRestoreScroll } from '../hooks/useRestoreScroll.js'
6
6
  import { cls } from '../../utils/cls.js'
@@ -11,41 +11,136 @@ type TChoiceGroup = {
11
11
  choices: string[]
12
12
  default: string
13
13
  disabled: string[]
14
+ hidden: boolean
15
+ lvl: number
14
16
  }
15
17
 
16
- function ChoiceGroup({
17
- children,
18
- choiceGroup,
19
- lvl,
20
- hide = false,
21
- }: { children: React.ReactNode; choiceGroup: TChoiceGroup; lvl: string; hide: boolean }) {
22
- const level = Number(lvl)
23
- const { name: groupName, choices, default: defaultChoice, disabled: disabledChoices } = choiceGroup
18
+ type ParentChoiceGroup = {
19
+ name: string
20
+ default: string
21
+ }
22
+
23
+ type ChoiceGroupWithParent = TChoiceGroup & { parentChoiceGroup?: ParentChoiceGroup & { choices: string[] } }
24
+
25
+ type ContextType = {
26
+ choiceGroupAll: ChoiceGroupWithParent[]
27
+ registerChoiceGroup: (choiceGroup: TChoiceGroup, parentChoiceGroup?: ParentChoiceGroup & { choice: string }) => void
28
+ }
29
+
30
+ const CustomSelectsContainerContext = createContext<ContextType | undefined>(undefined)
31
+
32
+ function useCustomSelectsContext() {
33
+ const ctx = useContext(CustomSelectsContainerContext)
34
+ if (!ctx) throw new Error('useCustomSelectsContext must be used inside provider')
35
+ return ctx
36
+ }
37
+
38
+ function CustomSelectsContainer({ children }: { children: React.ReactNode }) {
39
+ const [choiceGroupAll, setChoiceGroupAll] = useState<ChoiceGroupWithParent[]>([])
40
+
41
+ function registerChoiceGroup(choiceGroup: TChoiceGroup, parentChoiceGroup?: ParentChoiceGroup & { choice: string }) {
42
+ setChoiceGroupAll((prev) => {
43
+ const index = prev.findIndex((g) => g.name === choiceGroup.name)
44
+ const existing = prev[index]
45
+
46
+ if (!existing) {
47
+ return [
48
+ ...prev,
49
+ {
50
+ ...choiceGroup,
51
+ ...(parentChoiceGroup && {
52
+ parentChoiceGroup: {
53
+ ...parentChoiceGroup,
54
+ choices: [parentChoiceGroup.choice],
55
+ },
56
+ }),
57
+ },
58
+ ]
59
+ }
60
+
61
+ if (!parentChoiceGroup || !existing.parentChoiceGroup) return prev
62
+
63
+ const existingChoices = existing.parentChoiceGroup.choices
64
+ const mergedChoices = new Set([...existingChoices, parentChoiceGroup.choice])
65
+ if (mergedChoices.size === existingChoices.length) return prev
66
+
67
+ const next = [...prev]
68
+ next[index] = {
69
+ ...existing,
70
+ parentChoiceGroup: {
71
+ ...existing.parentChoiceGroup,
72
+ choices: [...mergedChoices],
73
+ },
74
+ }
75
+ return next
76
+ })
77
+ }
78
+
79
+ return (
80
+ <CustomSelectsContainerContext.Provider value={{ choiceGroupAll, registerChoiceGroup }}>
81
+ {children}
82
+ </CustomSelectsContainerContext.Provider>
83
+ )
84
+ }
85
+
86
+ type ChoiceGroupProps = {
87
+ children: React.ReactNode
88
+ choiceGroup: TChoiceGroup
89
+ parentChoiceGroup?: ParentChoiceGroup & { choice: string }
90
+ }
91
+
92
+ function ChoiceGroup({ children, choiceGroup, parentChoiceGroup }: ChoiceGroupProps) {
93
+ const { name: groupName, choices, default: defaultChoice, lvl } = choiceGroup
24
94
  // TODO/after-PR-merge rename useSelectedChoice useCurrentSelection
25
- const [selectedChoice, setSelectedChoice] = useSelectedChoice(groupName, defaultChoice)
26
- const prevPositionRef = useRestoreScroll([selectedChoice])
27
- const choiceGroupRef = useRef<HTMLDivElement>(null)
28
- const [rightOffset, setRightOffset] = useState(0)
95
+ const [selectedChoice] = useSelectedChoice(groupName, defaultChoice)
96
+ const { choiceGroupAll, registerChoiceGroup } = useCustomSelectsContext()
29
97
 
30
- useEffect(() => {
31
- if (level === 0 || !choiceGroupRef.current) return
32
- const parentCustomSelect = choiceGroupRef.current.closest(`[data-lvl="${level - 1}"]`)!.lastElementChild!
33
- const width = parentCustomSelect.getBoundingClientRect().width
98
+ useEffect(() => registerChoiceGroup(choiceGroup, parentChoiceGroup), [])
34
99
 
35
- setRightOffset(level * width + 2)
36
- }, [])
100
+ return (
101
+ <div data-choice-group={groupName} data-lvl={lvl} className="choice-group">
102
+ {/* Hidden select used to control choice visibility via CSS */}
103
+ <select name={`choicesFor-${groupName}`} value={selectedChoice} hidden disabled>
104
+ {choices.map((choice, i) => (
105
+ <option key={i} value={choice}>
106
+ {choice}
107
+ </option>
108
+ ))}
109
+ </select>
110
+ {children}
111
+ {lvl === 0 && (
112
+ <div className={`choice-group__selects`}>
113
+ {choiceGroupAll
114
+ .slice()
115
+ .sort((a, b) => a.lvl - b.lvl)
116
+ .map((choiceGroup, i) => (
117
+ <CustomSelect key={i} choiceGroup={choiceGroup} />
118
+ ))}
119
+ </div>
120
+ )}
121
+ </div>
122
+ )
123
+ }
37
124
 
38
- const isDisabled = (choice: string) => disabledChoices.includes(choice)
125
+ function CustomSelect({ choiceGroup }: { choiceGroup: ChoiceGroupWithParent }) {
126
+ const {
127
+ name: groupName,
128
+ choices,
129
+ default: defaultChoice,
130
+ disabled: disabledChoices,
131
+ hidden,
132
+ parentChoiceGroup,
133
+ } = choiceGroup
134
+ const [selectedChoice, setSelectedChoice] = useSelectedChoice(groupName, defaultChoice)
135
+ const prevPositionRef = useRestoreScroll([selectedChoice])
136
+ const [expanded, setExpanded] = useState(false)
39
137
  const selectedIndex = choices.indexOf(selectedChoice)
40
-
41
138
  const height = 25
42
- const [expanded, setExpanded] = useState(false)
43
139
  const rectTop = -selectedIndex * height
44
140
 
45
- // Cycle to next option
46
- const next = () => {
141
+ const isDisabled = (choice: string) => disabledChoices.includes(choice)
142
+ function next() {
47
143
  let nextIndex = selectedIndex
48
-
49
144
  for (let i = 0; i < choices.length; i++) {
50
145
  nextIndex = (nextIndex + 1) % choices.length
51
146
  if (!isDisabled(choices[nextIndex]!)) {
@@ -54,62 +149,57 @@ function ChoiceGroup({
54
149
  }
55
150
  }
56
151
  }
152
+ function isHidden() {
153
+ if (parentChoiceGroup) {
154
+ const [parentSelectedChoice] = useSelectedChoice(parentChoiceGroup.name, parentChoiceGroup.default)
155
+ return !parentChoiceGroup.choices.includes(parentSelectedChoice) || hidden
156
+ }
157
+ return hidden
158
+ }
57
159
 
58
160
  return (
59
- <div ref={choiceGroupRef} data-choice-group={groupName} data-lvl={level} className="choice-group">
60
- {/* Hidden select used to control choice visibility via CSS */}
61
- <select name={`choicesFor-${groupName}`} value={selectedChoice} hidden disabled>
62
- {choiceGroup.choices.map((choice, i) => (
63
- <option key={i} value={choice}>
64
- {choice}
65
- </option>
66
- ))}
67
- </select>
68
- {children}
161
+ <div
162
+ id={`choicesFor-${groupName}`}
163
+ aria-haspopup="listbox"
164
+ aria-expanded={expanded}
165
+ className={cls(['choice-select', (isHidden() || isDisabled(selectedChoice)) && 'hidden'])}
166
+ style={{ height }}
167
+ onMouseEnter={() => setExpanded(true)}
168
+ onMouseLeave={() => setExpanded(false)}
169
+ onClick={() => {
170
+ if (!expanded) next()
171
+ }}
172
+ >
69
173
  <div
70
- id={`choicesFor-${groupName}`}
71
- aria-haspopup="listbox"
72
- aria-expanded={expanded}
73
- className={cls(['select-container', (hide || isDisabled(selectedChoice)) && 'hidden'])}
74
- style={{ height, '--right-offset': `${rightOffset}px` }}
75
- onMouseEnter={() => setExpanded(true)}
76
- onMouseLeave={() => setExpanded(false)}
77
- onClick={() => {
78
- if (!expanded) next()
79
- }}
174
+ aria-activedescendant={`choice-${selectedChoice}`}
175
+ role="listbox"
176
+ className="choice-select__list"
177
+ style={{ top: rectTop, height: choices.length * height }}
80
178
  >
81
- <div
82
- aria-activedescendant={`choice-${selectedChoice}`}
83
- role="listbox"
84
- className="sliding-rectangle"
85
- style={{ top: rectTop, height: choices.length * height }}
86
- >
87
- {choices.map((choice, i) => (
88
- <div
89
- id={choice}
90
- key={i}
91
- aria-selected={i === selectedIndex}
92
- aria-disabled={isDisabled(choice)}
93
- role="option"
94
- className="select-choice"
95
- style={{ height }}
96
- onClick={handleOnClick}
97
- >
98
- <span>{choice}</span>
99
- </div>
100
- ))}
101
- </div>
179
+ {choices.map((choice, i) => (
180
+ <div
181
+ id={choice}
182
+ key={i}
183
+ aria-selected={i === selectedIndex}
184
+ aria-disabled={isDisabled(choice)}
185
+ role="option"
186
+ className="choice-select__option"
187
+ style={{ height }}
188
+ onClick={handleOnClick}
189
+ >
190
+ <span>{choice}</span>
191
+ </div>
192
+ ))}
102
193
  </div>
103
194
  </div>
104
195
  )
105
-
106
196
  function handleOnClick(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
107
197
  e.stopPropagation()
108
198
  const el = e.currentTarget
109
199
  prevPositionRef.current = { top: el.getBoundingClientRect().top, el }
110
- if (el.ariaSelected === 'true') {
200
+ if (el.getAttribute('aria-selected') === 'true') {
111
201
  next()
112
- } else if (el.ariaDisabled === 'false') {
202
+ } else if (el.getAttribute('aria-disabled') === 'false') {
113
203
  setSelectedChoice(el.id)
114
204
  }
115
205
  }
@@ -3,11 +3,12 @@ export { useMDXComponents }
3
3
  import React from 'react'
4
4
  import type { UseMdxComponents } from '@mdx-js/mdx'
5
5
  import { Pre } from '../components/Pre.js'
6
- import { ChoiceGroup } from '../components/ChoiceGroup.js'
6
+ import { ChoiceGroup, CustomSelectsContainer } from '../components/ChoiceGroup.js'
7
7
 
8
8
  const useMDXComponents: UseMdxComponents = () => {
9
9
  return {
10
10
  ChoiceGroup,
11
+ CustomSelectsContainer,
11
12
  pre: (props) => <Pre {...props} />,
12
13
  }
13
14
  }
@@ -101,5 +101,11 @@ declare module 'mdast' {
101
101
  export interface Data {
102
102
  customDataChoice?: string
103
103
  customDataFilter?: string
104
+ customDataParentChoiceGroup?: {
105
+ name: string
106
+ choice: string
107
+ default: string
108
+ lvl: number
109
+ }
104
110
  }
105
111
  }
@@ -70,8 +70,7 @@ function transformYaml(node: CodeNode) {
70
70
  { choiceValue: 'JavaScript', children: [yamlJsCode] },
71
71
  { choiceValue: 'TypeScript', children: [codeBlock] },
72
72
  ]
73
- const replacement = generateChoiceGroupCode(choiceNodes)
74
- replacement.attributes.push({ type: 'mdxJsxAttribute', name: 'hide' })
73
+ const replacement = generateChoiceGroupCode(choiceNodes, parent, true)
75
74
  replacement.data ??= { customDataChoice: choice, customDataFilter: 'codeLang' }
76
75
 
77
76
  parent.children.splice(index, 1, replacement)
@@ -142,12 +141,10 @@ async function transformTsToJs(node: CodeNode, file: VFile) {
142
141
  { choiceValue: 'JavaScript', children: [jsCode] },
143
142
  { choiceValue: 'TypeScript', children: [tsCode] },
144
143
  ]
145
- const replacement = generateChoiceGroupCode(choiceNodes)
144
+ // Add `hide` prop to `<ChoiceGroup>` if the only change was replacing `.ts` with `.js`
145
+ const hide = codeBlockReplacedJs === codeBlockContentJs
146
+ const replacement = generateChoiceGroupCode(choiceNodes, parent, hide)
146
147
 
147
- // Add `hide` attribute (prop) to `<ChoiceGroup>` if the only change was replacing `.ts` with `.js`
148
- if (codeBlockReplacedJs === codeBlockContentJs) {
149
- replacement.attributes.push({ type: 'mdxJsxAttribute', name: 'hide' })
150
- }
151
148
  replacement.data ??= { ...data }
152
149
 
153
150
  parent.children.splice(index, 1, replacement)
@@ -47,7 +47,7 @@ function remarkPkgManager() {
47
47
  }
48
48
 
49
49
  const choiceNodes = [...nodes].map(([name, node]) => ({ choiceValue: name, children: [node] }))
50
- const replacement = generateChoiceGroupCode(choiceNodes)
50
+ const replacement = generateChoiceGroupCode(choiceNodes, parent)
51
51
 
52
52
  replacement.data ??= { customDataChoice: choice, customDataFilter: replacement.type }
53
53
  parent.children.splice(index, 1, replacement)
@@ -24,7 +24,9 @@ const CHOICES_BUILT_IN: Record<string, { choices: string[]; default: string }> =
24
24
  },
25
25
  }
26
26
 
27
- function generateChoiceGroupCode(choiceNodes: ChoiceNode[], parent?: Parent): MdxJsxFlowElement {
27
+ function generateChoiceGroupCode(choiceNodes: ChoiceNode[], parent: Parent, hide: boolean = false): MdxJsxFlowElement {
28
+ let lvl: number = parent.type === 'mdxJsxFlowElement' ? 1 : 0
29
+
28
30
  const vikeConfig = getVikeConfig()
29
31
  const choices = choiceNodes.map((choiceNode) => choiceNode.choiceValue)
30
32
  const choiceGroup = findChoiceGroup(vikeConfig, choices)
@@ -41,47 +43,6 @@ function generateChoiceGroupCode(choiceNodes: ChoiceNode[], parent?: Parent): Md
41
43
  const attributes: MdxJsxAttribute[] = []
42
44
  const children: MdxJsxFlowElement[] = []
43
45
 
44
- attributes.push({
45
- type: 'mdxJsxAttribute',
46
- name: 'choiceGroup',
47
- value: {
48
- type: 'mdxJsxAttributeValueExpression',
49
- value: '',
50
- data: {
51
- estree: {
52
- type: 'Program',
53
- sourceType: 'module',
54
- comments: [],
55
- body: [
56
- // @ts-ignore: Missing properties in type definition
57
- { type: 'ExpressionStatement', expression: valueToEstree(choiceGroup) },
58
- ],
59
- },
60
- },
61
- },
62
- })
63
-
64
- if (choiceNodes.length === 1) {
65
- attributes.push({ type: 'mdxJsxAttribute', name: 'hide' })
66
- }
67
-
68
- let initLvl: number
69
-
70
- switch (parent?.type) {
71
- case 'root':
72
- initLvl = 0
73
- break
74
- case 'mdxJsxFlowElement':
75
- initLvl = 1
76
- break
77
-
78
- default:
79
- initLvl = 0
80
- break
81
- }
82
-
83
- attributes.push({ type: 'mdxJsxAttribute', name: 'lvl', value: `${initLvl}` })
84
-
85
46
  for (const choiceNode of mergedChoiceNodes) {
86
47
  const choiceChildren: (BlockContent | DefinitionContent)[] = []
87
48
  if (choiceNode.children.every((node) => node.type === 'containerDirective')) {
@@ -89,7 +50,6 @@ function generateChoiceGroupCode(choiceNodes: ChoiceNode[], parent?: Parent): Md
89
50
  } else {
90
51
  choiceChildren.push(...choiceNode.children)
91
52
  }
92
- for (const child of choiceChildren) increaseLvl(child)
93
53
 
94
54
  children.push({
95
55
  type: 'mdxJsxFlowElement',
@@ -99,15 +59,87 @@ function generateChoiceGroupCode(choiceNodes: ChoiceNode[], parent?: Parent): Md
99
59
  { type: 'mdxJsxAttribute', name: 'className', value: 'choice' },
100
60
  ],
101
61
  children: choiceChildren,
62
+ data: {
63
+ customDataParentChoiceGroup: {
64
+ name: choiceGroup.name,
65
+ choice: choiceNode.choiceValue,
66
+ default: choiceGroup.default,
67
+ lvl,
68
+ },
69
+ },
70
+ })
71
+ }
72
+
73
+ if (parent.data?.customDataParentChoiceGroup) {
74
+ const { lvl: parentLvl, ...parentChoiceGroup } = parent.data.customDataParentChoiceGroup
75
+
76
+ attributes.push({
77
+ type: 'mdxJsxAttribute',
78
+ name: 'parentChoiceGroup',
79
+ value: {
80
+ type: 'mdxJsxAttributeValueExpression',
81
+ value: '',
82
+ data: {
83
+ estree: {
84
+ type: 'Program',
85
+ sourceType: 'module',
86
+ comments: [],
87
+ body: [
88
+ // @ts-ignore: Missing properties in type definition
89
+ {
90
+ type: 'ExpressionStatement',
91
+ expression: valueToEstree(parentChoiceGroup),
92
+ },
93
+ ],
94
+ },
95
+ },
96
+ },
102
97
  })
98
+
99
+ lvl += parentLvl
100
+ parent.data.customDataParentChoiceGroup = undefined
103
101
  }
104
102
 
105
- return {
103
+ attributes.push({
104
+ type: 'mdxJsxAttribute',
105
+ name: 'choiceGroup',
106
+ value: {
107
+ type: 'mdxJsxAttributeValueExpression',
108
+ value: '',
109
+ data: {
110
+ estree: {
111
+ type: 'Program',
112
+ sourceType: 'module',
113
+ comments: [],
114
+ body: [
115
+ // @ts-ignore: Missing properties in type definition
116
+ {
117
+ type: 'ExpressionStatement',
118
+ expression: valueToEstree({ ...choiceGroup, hidden: choiceNodes.length === 1 || hide, lvl }),
119
+ },
120
+ ],
121
+ },
122
+ },
123
+ },
124
+ })
125
+
126
+ const choiceGroupNode: MdxJsxFlowElement = {
106
127
  type: 'mdxJsxFlowElement',
107
128
  name: 'ChoiceGroup',
108
129
  attributes,
109
130
  children,
110
131
  }
132
+
133
+ if (lvl === 0) {
134
+ return {
135
+ type: 'mdxJsxFlowElement',
136
+ name: 'CustomSelectsContainer',
137
+ attributes: [],
138
+ children: [choiceGroupNode],
139
+ }
140
+ }
141
+
142
+ return choiceGroupNode
111
143
  }
112
144
 
113
145
  function findChoiceGroup(vikeConfig: VikeConfig, choices: string[]) {
@@ -133,13 +165,3 @@ function findChoiceGroup(vikeConfig: VikeConfig, choices: string[]) {
133
165
 
134
166
  return choiceGroup
135
167
  }
136
-
137
- function increaseLvl(node: BlockContent | DefinitionContent) {
138
- if (node.type === 'mdxJsxFlowElement' && node.name === 'ChoiceGroup') {
139
- const attribute = node.attributes.find(
140
- (attribute) => attribute.type === 'mdxJsxAttribute' && attribute.name === 'lvl',
141
- )
142
- const lvlValue = attribute?.value
143
- if (lvlValue) attribute.value = `${Number(lvlValue) + 1}`
144
- }
145
- }
@@ -5,5 +5,11 @@ declare module 'mdast' {
5
5
  interface Data {
6
6
  customDataChoice?: string;
7
7
  customDataFilter?: string;
8
+ customDataParentChoiceGroup?: {
9
+ name: string;
10
+ choice: string;
11
+ default: string;
12
+ lvl: number;
13
+ };
8
14
  }
9
15
  }
@@ -55,8 +55,7 @@ function transformYaml(node) {
55
55
  { choiceValue: 'JavaScript', children: [yamlJsCode] },
56
56
  { choiceValue: 'TypeScript', children: [codeBlock] },
57
57
  ];
58
- const replacement = generateChoiceGroupCode(choiceNodes);
59
- replacement.attributes.push({ type: 'mdxJsxAttribute', name: 'hide' });
58
+ const replacement = generateChoiceGroupCode(choiceNodes, parent, true);
60
59
  replacement.data ?? (replacement.data = { customDataChoice: choice, customDataFilter: 'codeLang' });
61
60
  parent.children.splice(index, 1, replacement);
62
61
  }
@@ -118,11 +117,9 @@ async function transformTsToJs(node, file) {
118
117
  { choiceValue: 'JavaScript', children: [jsCode] },
119
118
  { choiceValue: 'TypeScript', children: [tsCode] },
120
119
  ];
121
- const replacement = generateChoiceGroupCode(choiceNodes);
122
- // Add `hide` attribute (prop) to `<ChoiceGroup>` if the only change was replacing `.ts` with `.js`
123
- if (codeBlockReplacedJs === codeBlockContentJs) {
124
- replacement.attributes.push({ type: 'mdxJsxAttribute', name: 'hide' });
125
- }
120
+ // Add `hide` prop to `<ChoiceGroup>` if the only change was replacing `.ts` with `.js`
121
+ const hide = codeBlockReplacedJs === codeBlockContentJs;
122
+ const replacement = generateChoiceGroupCode(choiceNodes, parent, hide);
126
123
  replacement.data ?? (replacement.data = { ...data });
127
124
  parent.children.splice(index, 1, replacement);
128
125
  }
@@ -36,7 +36,7 @@ function remarkPkgManager() {
36
36
  });
37
37
  }
38
38
  const choiceNodes = [...nodes].map(([name, node]) => ({ choiceValue: name, children: [node] }));
39
- const replacement = generateChoiceGroupCode(choiceNodes);
39
+ const replacement = generateChoiceGroupCode(choiceNodes, parent);
40
40
  replacement.data ?? (replacement.data = { customDataChoice: choice, customDataFilter: replacement.type });
41
41
  parent.children.splice(index, 1, replacement);
42
42
  });
@@ -6,4 +6,4 @@ type ChoiceNode = {
6
6
  choiceValue: string;
7
7
  children: (BlockContent | DefinitionContent)[];
8
8
  };
9
- declare function generateChoiceGroupCode(choiceNodes: ChoiceNode[], parent?: Parent): MdxJsxFlowElement;
9
+ declare function generateChoiceGroupCode(choiceNodes: ChoiceNode[], parent: Parent, hide?: boolean): MdxJsxFlowElement;
@@ -12,7 +12,8 @@ const CHOICES_BUILT_IN = {
12
12
  default: 'npm',
13
13
  },
14
14
  };
15
- function generateChoiceGroupCode(choiceNodes, parent) {
15
+ function generateChoiceGroupCode(choiceNodes, parent, hide = false) {
16
+ let lvl = parent.type === 'mdxJsxFlowElement' ? 1 : 0;
16
17
  const vikeConfig = getVikeConfig();
17
18
  const choices = choiceNodes.map((choiceNode) => choiceNode.choiceValue);
18
19
  const choiceGroup = findChoiceGroup(vikeConfig, choices);
@@ -25,41 +26,6 @@ function generateChoiceGroupCode(choiceNodes, parent) {
25
26
  });
26
27
  const attributes = [];
27
28
  const children = [];
28
- attributes.push({
29
- type: 'mdxJsxAttribute',
30
- name: 'choiceGroup',
31
- value: {
32
- type: 'mdxJsxAttributeValueExpression',
33
- value: '',
34
- data: {
35
- estree: {
36
- type: 'Program',
37
- sourceType: 'module',
38
- comments: [],
39
- body: [
40
- // @ts-ignore: Missing properties in type definition
41
- { type: 'ExpressionStatement', expression: valueToEstree(choiceGroup) },
42
- ],
43
- },
44
- },
45
- },
46
- });
47
- if (choiceNodes.length === 1) {
48
- attributes.push({ type: 'mdxJsxAttribute', name: 'hide' });
49
- }
50
- let initLvl;
51
- switch (parent?.type) {
52
- case 'root':
53
- initLvl = 0;
54
- break;
55
- case 'mdxJsxFlowElement':
56
- initLvl = 1;
57
- break;
58
- default:
59
- initLvl = 0;
60
- break;
61
- }
62
- attributes.push({ type: 'mdxJsxAttribute', name: 'lvl', value: `${initLvl}` });
63
29
  for (const choiceNode of mergedChoiceNodes) {
64
30
  const choiceChildren = [];
65
31
  if (choiceNode.children.every((node) => node.type === 'containerDirective')) {
@@ -68,8 +34,6 @@ function generateChoiceGroupCode(choiceNodes, parent) {
68
34
  else {
69
35
  choiceChildren.push(...choiceNode.children);
70
36
  }
71
- for (const child of choiceChildren)
72
- increaseLvl(child);
73
37
  children.push({
74
38
  type: 'mdxJsxFlowElement',
75
39
  name: 'div',
@@ -78,14 +42,80 @@ function generateChoiceGroupCode(choiceNodes, parent) {
78
42
  { type: 'mdxJsxAttribute', name: 'className', value: 'choice' },
79
43
  ],
80
44
  children: choiceChildren,
45
+ data: {
46
+ customDataParentChoiceGroup: {
47
+ name: choiceGroup.name,
48
+ choice: choiceNode.choiceValue,
49
+ default: choiceGroup.default,
50
+ lvl,
51
+ },
52
+ },
81
53
  });
82
54
  }
83
- return {
55
+ if (parent.data?.customDataParentChoiceGroup) {
56
+ const { lvl: parentLvl, ...parentChoiceGroup } = parent.data.customDataParentChoiceGroup;
57
+ attributes.push({
58
+ type: 'mdxJsxAttribute',
59
+ name: 'parentChoiceGroup',
60
+ value: {
61
+ type: 'mdxJsxAttributeValueExpression',
62
+ value: '',
63
+ data: {
64
+ estree: {
65
+ type: 'Program',
66
+ sourceType: 'module',
67
+ comments: [],
68
+ body: [
69
+ // @ts-ignore: Missing properties in type definition
70
+ {
71
+ type: 'ExpressionStatement',
72
+ expression: valueToEstree(parentChoiceGroup),
73
+ },
74
+ ],
75
+ },
76
+ },
77
+ },
78
+ });
79
+ lvl += parentLvl;
80
+ parent.data.customDataParentChoiceGroup = undefined;
81
+ }
82
+ attributes.push({
83
+ type: 'mdxJsxAttribute',
84
+ name: 'choiceGroup',
85
+ value: {
86
+ type: 'mdxJsxAttributeValueExpression',
87
+ value: '',
88
+ data: {
89
+ estree: {
90
+ type: 'Program',
91
+ sourceType: 'module',
92
+ comments: [],
93
+ body: [
94
+ // @ts-ignore: Missing properties in type definition
95
+ {
96
+ type: 'ExpressionStatement',
97
+ expression: valueToEstree({ ...choiceGroup, hidden: choiceNodes.length === 1 || hide, lvl }),
98
+ },
99
+ ],
100
+ },
101
+ },
102
+ },
103
+ });
104
+ const choiceGroupNode = {
84
105
  type: 'mdxJsxFlowElement',
85
106
  name: 'ChoiceGroup',
86
107
  attributes,
87
108
  children,
88
109
  };
110
+ if (lvl === 0) {
111
+ return {
112
+ type: 'mdxJsxFlowElement',
113
+ name: 'CustomSelectsContainer',
114
+ attributes: [],
115
+ children: [choiceGroupNode],
116
+ };
117
+ }
118
+ return choiceGroupNode;
89
119
  }
90
120
  function findChoiceGroup(vikeConfig, choices) {
91
121
  const { choices: choicesConfig } = vikeConfig.config.docpress;
@@ -107,11 +137,3 @@ function findChoiceGroup(vikeConfig, choices) {
107
137
  };
108
138
  return choiceGroup;
109
139
  }
110
- function increaseLvl(node) {
111
- if (node.type === 'mdxJsxFlowElement' && node.name === 'ChoiceGroup') {
112
- const attribute = node.attributes.find((attribute) => attribute.type === 'mdxJsxAttribute' && attribute.name === 'lvl');
113
- const lvlValue = attribute?.value;
114
- if (lvlValue)
115
- attribute.value = `${Number(lvlValue) + 1}`;
116
- }
117
- }
@@ -31,7 +31,7 @@ const prettyCode = [
31
31
  },
32
32
  ];
33
33
  const rehypePlugins = [prettyCode, [rehypeMetaToProps]];
34
- const remarkPlugins = [remarkGfm, remarkDirective, remarkDetype, remarkPkgManager, remarkChoiceGroup];
34
+ const remarkPlugins = [remarkGfm, remarkDirective, remarkChoiceGroup, remarkDetype, remarkPkgManager];
35
35
  const config = {
36
36
  root,
37
37
  plugins: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brillout/docpress",
3
- "version": "0.16.30",
3
+ "version": "0.16.31",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@brillout/picocolors": "^1.0.10",
package/vite.config.ts CHANGED
@@ -34,7 +34,7 @@ const prettyCode = [
34
34
  },
35
35
  ]
36
36
  const rehypePlugins: any = [prettyCode, [rehypeMetaToProps]]
37
- const remarkPlugins = [remarkGfm, remarkDirective, remarkDetype, remarkPkgManager, remarkChoiceGroup]
37
+ const remarkPlugins = [remarkGfm, remarkDirective, remarkChoiceGroup, remarkDetype, remarkPkgManager]
38
38
 
39
39
  const config: UserConfig = {
40
40
  root,