@astryxdesign/core 0.7.7-canary.4a8a76e → 0.7.7-canary.4e495ec

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.
Files changed (266) hide show
  1. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
  2. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
  3. package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
  4. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  5. package/dist/BottomSheet/BottomSheet.js +79 -9
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.js +1 -1
  8. package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
  9. package/dist/BottomSheet/bottomSheetStack.js +5 -4
  10. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
  13. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  14. package/dist/CheckboxList/CheckboxList.js +7 -1
  15. package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
  16. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  17. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  18. package/dist/CheckboxList/CheckboxListItem.js +10 -2
  19. package/dist/DateInput/DateInput.d.ts.map +1 -1
  20. package/dist/DateInput/DateInput.js +5 -2
  21. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  22. package/dist/DateTimeInput/DateTimeInput.js +5 -2
  23. package/dist/Dialog/Dialog.d.ts.map +1 -1
  24. package/dist/Dialog/Dialog.js +16 -14
  25. package/dist/Drawer/Drawer.d.ts.map +1 -1
  26. package/dist/Drawer/Drawer.js +15 -11
  27. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  28. package/dist/Drawer/useDrawerDialogPresence.js +5 -1
  29. package/dist/Field/PanelSearchInput.d.ts +9 -1
  30. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  31. package/dist/Field/PanelSearchInput.js +3 -1
  32. package/dist/InfoTip/InfoTip.d.ts +2 -2
  33. package/dist/InfoTip/InfoTip.js +4 -4
  34. package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
  35. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  36. package/dist/Layer/layerAnimations.stylex.js +25 -20
  37. package/dist/Layer/useBackdropPress.d.ts +27 -0
  38. package/dist/Layer/useBackdropPress.d.ts.map +1 -0
  39. package/dist/Layer/useBackdropPress.js +53 -0
  40. package/dist/Layer/usePressPin.d.ts +3 -5
  41. package/dist/Layer/usePressPin.d.ts.map +1 -1
  42. package/dist/Layer/usePressPin.js +4 -6
  43. package/dist/MobileNav/MobileNav.js +0 -1
  44. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  45. package/dist/MultiSelector/hooks.js +2 -4
  46. package/dist/Outline/Outline.d.ts +2 -4
  47. package/dist/Outline/Outline.d.ts.map +1 -1
  48. package/dist/Outline/Outline.js +4 -52
  49. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  50. package/dist/Outline/useOutlineFromMarkdown.js +1 -0
  51. package/dist/Outline/useScrollSpy.js +3 -3
  52. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  53. package/dist/PowerSearch/PowerSearch.js +134 -179
  54. package/dist/RadioList/RadioList.d.ts.map +1 -1
  55. package/dist/RadioList/RadioList.js +5 -0
  56. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  57. package/dist/Resizable/ResizeHandle.js +118 -260
  58. package/dist/Resizable/resizeGesture.d.ts +91 -0
  59. package/dist/Resizable/resizeGesture.d.ts.map +1 -0
  60. package/dist/Resizable/resizeGesture.js +251 -0
  61. package/dist/SegmentedControl/SegmentedControl.d.ts +3 -1
  62. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  63. package/dist/SegmentedControl/SegmentedControl.js +115 -8
  64. package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
  65. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  66. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -0
  67. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  68. package/dist/SegmentedControl/SegmentedControlItem.js +8 -1
  69. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts +357 -0
  70. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -0
  71. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +1349 -0
  72. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts +70 -0
  73. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts.map +1 -0
  74. package/dist/SegmentedControl/useSegmentedTouchTracking.js +264 -0
  75. package/dist/Selector/Selector.d.ts.map +1 -1
  76. package/dist/Selector/Selector.js +96 -67
  77. package/dist/Selector/optionShape.d.ts +24 -0
  78. package/dist/Selector/optionShape.d.ts.map +1 -0
  79. package/dist/Selector/optionShape.js +48 -0
  80. package/dist/Selector/selectableOptionEntries.d.ts +20 -0
  81. package/dist/Selector/selectableOptionEntries.d.ts.map +1 -0
  82. package/dist/Selector/selectableOptionEntries.js +38 -0
  83. package/dist/Selector/utils.d.ts +2 -17
  84. package/dist/Selector/utils.d.ts.map +1 -1
  85. package/dist/Selector/utils.js +3 -51
  86. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  87. package/dist/SideNav/SideNavItem.js +3 -9
  88. package/dist/Switch/switchTouch.d.ts +0 -7
  89. package/dist/Switch/switchTouch.d.ts.map +1 -1
  90. package/dist/Switch/switchTouch.js +2 -10
  91. package/dist/Switch/useSwitchTouch.d.ts +2 -11
  92. package/dist/Switch/useSwitchTouch.d.ts.map +1 -1
  93. package/dist/Switch/useSwitchTouch.js +26 -85
  94. package/dist/Token/Token.d.ts +2 -2
  95. package/dist/Token/Token.d.ts.map +1 -1
  96. package/dist/Token/Token.js +12 -4
  97. package/dist/Token/TokenNamingContext.d.ts +14 -0
  98. package/dist/Token/TokenNamingContext.d.ts.map +1 -0
  99. package/dist/Token/TokenNamingContext.js +23 -0
  100. package/dist/Tokenizer/Tokenizer.d.ts +46 -59
  101. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  102. package/dist/Tokenizer/Tokenizer.js +35 -58
  103. package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
  104. package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
  105. package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
  106. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -1
  107. package/dist/Tokenizer/TokenizerItemRemovableContext.js +5 -3
  108. package/dist/Tooltip/Tooltip.d.ts +25 -30
  109. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  110. package/dist/Tooltip/Tooltip.js +24 -5
  111. package/dist/Tooltip/index.d.ts +1 -1
  112. package/dist/Tooltip/index.d.ts.map +1 -1
  113. package/dist/Tooltip/useTooltip.d.ts +28 -29
  114. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  115. package/dist/Tooltip/useTooltip.js +20 -8
  116. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  117. package/dist/TopNav/TopNavItem.js +4 -7
  118. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  119. package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
  120. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  121. package/dist/TopNav/TopNavMenu.js +4 -7
  122. package/dist/astryx.css +76 -5
  123. package/dist/gestures/GestureHost.d.ts.map +1 -1
  124. package/dist/gestures/GestureHost.js +53 -5
  125. package/dist/hooks/actionGroupTracker.d.ts +3 -3
  126. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  127. package/dist/hooks/actionGroupTracker.js +6 -53
  128. package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
  129. package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
  130. package/dist/hooks/useHighlightedOptionScroll.js +112 -11
  131. package/dist/hooks/useMediaQuery.d.ts +2 -1
  132. package/dist/hooks/useMediaQuery.d.ts.map +1 -1
  133. package/dist/hooks/useMediaQuery.js +7 -4
  134. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  135. package/dist/hooks/useMenuPress.js +4 -2
  136. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
  137. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
  138. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
  139. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
  140. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  141. package/dist/i18n/generated-locales/en.generated.js +1 -0
  142. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  143. package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
  144. package/dist/internal/SheetConfirmation.d.ts +11 -0
  145. package/dist/internal/SheetConfirmation.d.ts.map +1 -0
  146. package/dist/internal/SheetConfirmation.js +92 -0
  147. package/dist/internal/sheetDraft.d.ts +35 -0
  148. package/dist/internal/sheetDraft.d.ts.map +1 -0
  149. package/dist/internal/sheetDraft.js +65 -0
  150. package/dist/internal/touch-sheet.d.ts +21 -0
  151. package/dist/internal/touch-sheet.d.ts.map +1 -0
  152. package/dist/internal/touch-sheet.js +19 -0
  153. package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
  154. package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
  155. package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
  156. package/dist/motion/roles.d.ts +7 -1
  157. package/dist/motion/roles.d.ts.map +1 -1
  158. package/dist/motion/roles.js +20 -1
  159. package/dist/motion/uikit/index.d.ts +6 -5
  160. package/dist/motion/uikit/index.d.ts.map +1 -1
  161. package/dist/motion/uikit/index.js +6 -5
  162. package/dist/motion/uikit/segmented.d.ts +55 -0
  163. package/dist/motion/uikit/segmented.d.ts.map +1 -0
  164. package/dist/motion/uikit/segmented.js +77 -0
  165. package/dist/motion/uikit/springs.d.ts +0 -18
  166. package/dist/motion/uikit/springs.d.ts.map +1 -1
  167. package/dist/motion/uikit/springs.js +5 -21
  168. package/dist/privateAttributes/segmentTitle.stylex.d.ts +5 -0
  169. package/dist/privateAttributes/segmentTitle.stylex.d.ts.map +1 -0
  170. package/dist/privateAttributes/segmentTitle.stylex.js +22 -0
  171. package/dist/utils/browserClickWait.d.ts +35 -0
  172. package/dist/utils/browserClickWait.d.ts.map +1 -0
  173. package/dist/utils/browserClickWait.js +123 -0
  174. package/dist/utils/clickGuard.d.ts +8 -1
  175. package/dist/utils/clickGuard.d.ts.map +1 -1
  176. package/dist/utils/clickGuard.js +24 -1
  177. package/locales/en.json +4 -0
  178. package/locales/pseudo.json +3 -0
  179. package/package.json +8 -8
  180. package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
  181. package/src/BottomSheet/BottomSheet.tsx +95 -10
  182. package/src/BottomSheet/BottomSheetPanel.tsx +7 -2
  183. package/src/BottomSheet/bottomSheetStack.ts +8 -4
  184. package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
  185. package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
  186. package/src/CheckboxList/CheckboxList.tsx +6 -0
  187. package/src/CheckboxList/CheckboxListContext.tsx +6 -0
  188. package/src/CheckboxList/CheckboxListItem.tsx +11 -2
  189. package/src/DateInput/DateInput.tsx +8 -2
  190. package/src/DateTimeInput/DateTimeInput.tsx +8 -2
  191. package/src/Dialog/Dialog.tsx +44 -14
  192. package/src/Drawer/Drawer.tsx +28 -11
  193. package/src/Drawer/useDrawerDialogPresence.ts +5 -1
  194. package/src/Field/PanelSearchInput.tsx +11 -0
  195. package/src/InfoTip/InfoTip.doc.mjs +3 -3
  196. package/src/InfoTip/InfoTip.tsx +5 -5
  197. package/src/Layer/layerAnimations.stylex.ts +24 -16
  198. package/src/Layer/useBackdropPress.ts +81 -0
  199. package/src/Layer/usePressPin.ts +5 -11
  200. package/src/MobileNav/MobileNav.tsx +1 -1
  201. package/src/MultiSelector/hooks.ts +2 -4
  202. package/src/Outline/Outline.doc.mjs +177 -51
  203. package/src/Outline/Outline.tsx +5 -57
  204. package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
  205. package/src/Outline/useOutlineFromMarkdown.ts +1 -0
  206. package/src/Outline/useScrollSpy.ts +3 -3
  207. package/src/PowerSearch/PowerSearch.doc.mjs +3 -3
  208. package/src/PowerSearch/PowerSearch.tsx +196 -183
  209. package/src/RadioList/RadioList.doc.mjs +4 -2
  210. package/src/RadioList/RadioList.tsx +4 -0
  211. package/src/Resizable/ResizeHandle.tsx +133 -328
  212. package/src/Resizable/resizeGesture.ts +368 -0
  213. package/src/SegmentedControl/SegmentedControl.doc.mjs +3 -3
  214. package/src/SegmentedControl/SegmentedControl.tsx +179 -5
  215. package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
  216. package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
  217. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +1637 -0
  218. package/src/SegmentedControl/useSegmentedTouchTracking.ts +308 -0
  219. package/src/Selector/Selector.tsx +136 -89
  220. package/src/Selector/optionShape.ts +64 -0
  221. package/src/Selector/selectableOptionEntries.ts +39 -0
  222. package/src/Selector/utils.ts +9 -68
  223. package/src/SideNav/SideNavItem.tsx +3 -7
  224. package/src/Switch/switchTouch.ts +2 -10
  225. package/src/Switch/useSwitchTouch.ts +26 -96
  226. package/src/Token/Token.tsx +13 -4
  227. package/src/Token/TokenNamingContext.ts +28 -0
  228. package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
  229. package/src/Tokenizer/Tokenizer.tsx +81 -86
  230. package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
  231. package/src/Tokenizer/TokenizerItemRemovableContext.ts +5 -3
  232. package/src/Tooltip/Tooltip.doc.mjs +123 -54
  233. package/src/Tooltip/Tooltip.tsx +36 -39
  234. package/src/Tooltip/index.ts +0 -1
  235. package/src/Tooltip/useTooltip.doc.mjs +147 -33
  236. package/src/Tooltip/useTooltip.tsx +39 -32
  237. package/src/TopNav/TopNavItem.tsx +6 -5
  238. package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
  239. package/src/TopNav/TopNavMenu.tsx +6 -5
  240. package/src/gestures/GestureHost.ts +73 -4
  241. package/src/hooks/actionGroupTracker.ts +12 -56
  242. package/src/hooks/useHighlightedOptionScroll.ts +123 -15
  243. package/src/hooks/useMediaQuery.doc.mjs +2 -2
  244. package/src/hooks/useMediaQuery.ts +13 -4
  245. package/src/hooks/useMenuPress.ts +8 -2
  246. package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
  247. package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
  248. package/src/i18n/generated-locales/en.generated.ts +1 -0
  249. package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
  250. package/src/internal/SheetConfirmation.tsx +101 -0
  251. package/src/internal/sheetDraft.ts +80 -0
  252. package/src/internal/touch-sheet.ts +21 -0
  253. package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
  254. package/src/motion/MEASUREMENTS.md +7 -0
  255. package/src/motion/README.md +1 -0
  256. package/src/motion/roles.ts +20 -1
  257. package/src/motion/uikit/index.ts +5 -5
  258. package/src/motion/uikit/segmented.ts +74 -0
  259. package/src/motion/uikit/springs.ts +5 -21
  260. package/src/privateAttributes/segmentTitle.stylex.ts +22 -0
  261. package/src/utils/browserClickWait.ts +133 -0
  262. package/src/utils/clickGuard.ts +37 -1
  263. package/dist/internal/tokenizer-presentation.d.ts +0 -21
  264. package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
  265. package/dist/internal/tokenizer-presentation.js +0 -21
  266. package/src/internal/tokenizer-presentation.ts +0 -25
@@ -63,7 +63,11 @@ export const docs = {
63
63
  targets: [
64
64
  {className: 'astryx-outline', visualProps: ['density']},
65
65
  {className: 'astryx-outline-indicator'},
66
- {className: 'astryx-outline-item', visualProps: ['level'], states: ['active']},
66
+ {
67
+ className: 'astryx-outline-item',
68
+ visualProps: ['level'],
69
+ states: ['active'],
70
+ },
67
71
  ],
68
72
  },
69
73
  components: [
@@ -101,7 +105,8 @@ export const docs = {
101
105
  {
102
106
  name: 'density',
103
107
  type: "'default' | 'compact'",
104
- description: "Density variant controlling item padding. 'compact' for dense UIs, 'default' for standard spacing.",
108
+ description:
109
+ "Density variant controlling item padding. 'compact' for dense UIs, 'default' for standard spacing.",
105
110
  default: "'default'",
106
111
  },
107
112
  {
@@ -271,17 +276,53 @@ function FlashOnArrival() {
271
276
  usage: {
272
277
  anatomy,
273
278
  description:
274
- 'A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes. Features a sliding indicator track that animates to the active heading. The list is a single tab stop: arrow keys move between headings, Home/End jump to the ends, and Enter/Space activate.',
279
+ 'A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes. Features a sliding indicator track that animates to the active heading. Each link is in the normal Tab order, and Enter/Space activate.',
275
280
  bestPractices: [
276
- {guidance: true, description: 'Pass a flat ordered list of headings and let level control indentation.'},
277
- {guidance: true, description: 'Use activeId when custom scroll logic owns the active section.'},
278
- {guidance: true, description: 'Use density="compact" in dense sidebars where vertical space is tight.'},
279
- {guidance: true, description: 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content. For linkable Markdown headings, pass the same createMarkdownHeadingLinks() entry to Markdown and Outline; configure headingIdPrefix on that plugin for multiple roots.'},
280
- {guidance: true, description: 'Pass scrollContainerRef when the content scrolls in a split pane, modal, or panel instead of the viewport.'},
281
- {guidance: true, description: 'Set offset to the height of a fixed header that overlays the content, so headings land below it instead of underneath it.'},
282
- {guidance: false, description: 'Use Outline for application navigation - use SideNav or TopNav for routes.'},
283
- {guidance: false, description: 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.'},
284
- {guidance: false, description: 'Rely on onNavigateEnd to mean "arrived" - it also fires when the user interrupts the scroll.'},
281
+ {
282
+ guidance: true,
283
+ description:
284
+ 'Pass a flat ordered list of headings and let level control indentation.',
285
+ },
286
+ {
287
+ guidance: true,
288
+ description:
289
+ 'Use activeId when custom scroll logic owns the active section.',
290
+ },
291
+ {
292
+ guidance: true,
293
+ description:
294
+ 'Use density="compact" in dense sidebars where vertical space is tight.',
295
+ },
296
+ {
297
+ guidance: true,
298
+ description:
299
+ 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content. For linkable Markdown headings, pass the same createMarkdownHeadingLinks() entry to Markdown and Outline; configure headingIdPrefix on that plugin for multiple roots.',
300
+ },
301
+ {
302
+ guidance: true,
303
+ description:
304
+ 'Pass scrollContainerRef when the content scrolls in a split pane, modal, or panel instead of the viewport.',
305
+ },
306
+ {
307
+ guidance: true,
308
+ description:
309
+ 'Set offset to the height of a fixed header that overlays the content, so headings land below it instead of underneath it.',
310
+ },
311
+ {
312
+ guidance: false,
313
+ description:
314
+ 'Use Outline for application navigation - use SideNav or TopNav for routes.',
315
+ },
316
+ {
317
+ guidance: false,
318
+ description:
319
+ 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.',
320
+ },
321
+ {
322
+ guidance: false,
323
+ description:
324
+ 'Rely on onNavigateEnd to mean "arrived" - it also fires when the user interrupts the scroll.',
325
+ },
285
326
  ],
286
327
  },
287
328
  };
@@ -295,7 +336,11 @@ export const docsZh = {
295
336
  targets: [
296
337
  {className: 'astryx-outline', visualProps: ['density']},
297
338
  {className: 'astryx-outline-indicator'},
298
- {className: 'astryx-outline-item', visualProps: ['level'], states: ['active']},
339
+ {
340
+ className: 'astryx-outline-item',
341
+ visualProps: ['level'],
342
+ states: ['active'],
343
+ },
299
344
  ],
300
345
  },
301
346
  components: [
@@ -332,40 +377,47 @@ export const docsZh = {
332
377
  {
333
378
  name: 'density',
334
379
  type: "'default' | 'compact'",
335
- description: "控制项目内边距的密度变体。'compact' 用于紧凑界面,'default' 为标准间距。",
380
+ description:
381
+ "控制项目内边距的密度变体。'compact' 用于紧凑界面,'default' 为标准间距。",
336
382
  default: "'default'",
337
383
  },
338
384
  {
339
385
  name: 'onNavigateStart',
340
386
  type: '(id: string) => void',
341
- description: '导航开始时(滚动前)调用,传入项目 id。与 onNavigateEnd 搭配可在目标标题上实现到达效果(闪烁、描边、脉冲)。',
387
+ description:
388
+ '导航开始时(滚动前)调用,传入项目 id。与 onNavigateEnd 搭配可在目标标题上实现到达效果(闪烁、描边、脉冲)。',
342
389
  },
343
390
  {
344
391
  name: 'onNavigateEnd',
345
392
  type: '(id: string) => void',
346
- description: '导航结束时调用(平滑滚动停止,或减弱动效将其变为瞬时跳转)。每次 onNavigateStart 必定对应一次调用,包括用户中途手动滚动打断的情况,因此“导航中”状态不会泄漏。',
393
+ description:
394
+ '导航结束时调用(平滑滚动停止,或减弱动效将其变为瞬时跳转)。每次 onNavigateStart 必定对应一次调用,包括用户中途手动滚动打断的情况,因此“导航中”状态不会泄漏。',
347
395
  },
348
396
  {
349
397
  name: 'offset',
350
398
  type: 'number',
351
- description: '覆盖滚动根顶部的固定顶栏高度(px)。同时位移激活线与滚动落点,因此标题被激活的位置正是点击后停留的位置——落在顶栏下方,而非被顶栏遮住。它与每个标题自身的 scroll-margin-top 叠加(先顶栏,再留白),而非取代它。若没有元素覆盖内容,保持 0,交给 scroll-margin-top 处理。',
399
+ description:
400
+ '覆盖滚动根顶部的固定顶栏高度(px)。同时位移激活线与滚动落点,因此标题被激活的位置正是点击后停留的位置——落在顶栏下方,而非被顶栏遮住。它与每个标题自身的 scroll-margin-top 叠加(先顶栏,再留白),而非取代它。若没有元素覆盖内容,保持 0,交给 scroll-margin-top 处理。',
352
401
  default: '0',
353
402
  },
354
403
  {
355
404
  name: 'scrollContainerRef',
356
405
  type: 'React.RefObject<HTMLElement | null>',
357
- description: '要跟踪的滚动容器,替代自动探测最近的可滚动祖先。当内容在分栏、弹窗或面板中滚动而非视口时使用。',
406
+ description:
407
+ '要跟踪的滚动容器,替代自动探测最近的可滚动祖先。当内容在分栏、弹窗或面板中滚动而非视口时使用。',
358
408
  },
359
409
  {
360
410
  name: 'hasScrollOnClick',
361
411
  type: 'boolean',
362
- description: '激活项目时是否平滑滚动。设为 false 可自行接管滚动(虚拟列表、路由):Outline 仍会更新激活项、hash 和导航回调,但不执行滚动。',
412
+ description:
413
+ '激活项目时是否平滑滚动。设为 false 可自行接管滚动(虚拟列表、路由):Outline 仍会更新激活项、hash 和导航回调,但不执行滚动。',
363
414
  default: 'true',
364
415
  },
365
416
  {
366
417
  name: 'xstyle',
367
418
  type: 'StyleXStyles',
368
- description: '用于布局自定义的 StyleX 样式。必须是 stylex.create() 值。',
419
+ description:
420
+ '用于布局自定义的 StyleX 样式。必须是 stylex.create() 值。',
369
421
  },
370
422
  ],
371
423
  },
@@ -375,12 +427,36 @@ export const docsZh = {
375
427
  description:
376
428
  'A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes.',
377
429
  bestPractices: [
378
- {guidance: true, description: 'Pass a flat ordered list of headings and let level control indentation.'},
379
- {guidance: true, description: 'Use activeId when custom scroll logic owns the active section.'},
380
- {guidance: true, description: 'Use density="compact" in dense sidebars where vertical space is tight.'},
381
- {guidance: true, description: 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content. For linkable Markdown headings, pass the same createMarkdownHeadingLinks() entry to Markdown and Outline; configure headingIdPrefix on that plugin for multiple roots.'},
382
- {guidance: false, description: 'Use Outline for application navigation - use SideNav or TopNav for routes.'},
383
- {guidance: false, description: 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.'},
430
+ {
431
+ guidance: true,
432
+ description:
433
+ 'Pass a flat ordered list of headings and let level control indentation.',
434
+ },
435
+ {
436
+ guidance: true,
437
+ description:
438
+ 'Use activeId when custom scroll logic owns the active section.',
439
+ },
440
+ {
441
+ guidance: true,
442
+ description:
443
+ 'Use density="compact" in dense sidebars where vertical space is tight.',
444
+ },
445
+ {
446
+ guidance: true,
447
+ description:
448
+ 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content. For linkable Markdown headings, pass the same createMarkdownHeadingLinks() entry to Markdown and Outline; configure headingIdPrefix on that plugin for multiple roots.',
449
+ },
450
+ {
451
+ guidance: false,
452
+ description:
453
+ 'Use Outline for application navigation - use SideNav or TopNav for routes.',
454
+ },
455
+ {
456
+ guidance: false,
457
+ description:
458
+ 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.',
459
+ },
384
460
  ],
385
461
  },
386
462
  };
@@ -388,34 +464,77 @@ export const docsZh = {
388
464
  /** @type {import('@astryxdesign/cli/authoring').ComponentTranslationDoc} */
389
465
  export const docsDense = {
390
466
  description:
391
- 'Document outline/table-of-contents nav with sliding indicator track. Flat items array {id,label,level}; anchor links; density variant (default/compact); uncontrolled scroll-spy by scroll position (last heading past its resting line = offset + its own scroll-margin-top; first item at top, last at bottom); controlled with activeId; smooth-scroll on click that pins the active item until the next manual scroll. Single tab stop (roving tabindex): Arrow keys move, Home/End jump, Enter/Space activate. onNavigateStart/onNavigateEnd fire around the scroll. Scroll scoping via offset / scrollContainerRef / hasScrollOnClick.',
467
+ 'Document outline/table-of-contents nav with sliding indicator track. Flat items array {id,label,level}; anchor links; density variant (default/compact); uncontrolled scroll-spy by scroll position (last heading past its resting line = offset + its own scroll-margin-top; first item at top, last at bottom); controlled with activeId; smooth-scroll on click that pins the active item until the next manual scroll. Each link is a Tab stop; Enter/Space activate; no arrow/Home/End movement. onNavigateStart/onNavigateEnd fire around the scroll. Scroll scoping via offset / scrollContainerRef / hasScrollOnClick.',
392
468
  usage: {
393
469
  anatomy,
394
470
  description:
395
471
  'A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes.',
396
472
  bestPractices: [
397
- {guidance: true, description: 'Pass a flat ordered list of headings and let level control indentation.'},
398
- {guidance: true, description: 'Use activeId when custom scroll logic owns the active section.'},
399
- {guidance: true, description: 'Use density="compact" in dense sidebars where vertical space is tight.'},
400
- {guidance: true, description: 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content. For linkable Markdown headings, pass the same createMarkdownHeadingLinks() entry to Markdown and Outline; configure headingIdPrefix on that plugin for multiple roots.'},
401
- {guidance: true, description: 'Pass scrollContainerRef when the content scrolls in a split pane, modal, or panel instead of the viewport.'},
402
- {guidance: true, description: 'Set offset to the height of a fixed header that overlays the content, so headings land below it instead of underneath it.'},
403
- {guidance: false, description: 'Use Outline for application navigation - use SideNav or TopNav for routes.'},
404
- {guidance: false, description: 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.'},
405
- {guidance: false, description: 'Rely on onNavigateEnd to mean "arrived" - it also fires when the user interrupts the scroll.'},
473
+ {
474
+ guidance: true,
475
+ description:
476
+ 'Pass a flat ordered list of headings and let level control indentation.',
477
+ },
478
+ {
479
+ guidance: true,
480
+ description:
481
+ 'Use activeId when custom scroll logic owns the active section.',
482
+ },
483
+ {
484
+ guidance: true,
485
+ description:
486
+ 'Use density="compact" in dense sidebars where vertical space is tight.',
487
+ },
488
+ {
489
+ guidance: true,
490
+ description:
491
+ 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content. For linkable Markdown headings, pass the same createMarkdownHeadingLinks() entry to Markdown and Outline; configure headingIdPrefix on that plugin for multiple roots.',
492
+ },
493
+ {
494
+ guidance: true,
495
+ description:
496
+ 'Pass scrollContainerRef when the content scrolls in a split pane, modal, or panel instead of the viewport.',
497
+ },
498
+ {
499
+ guidance: true,
500
+ description:
501
+ 'Set offset to the height of a fixed header that overlays the content, so headings land below it instead of underneath it.',
502
+ },
503
+ {
504
+ guidance: false,
505
+ description:
506
+ 'Use Outline for application navigation - use SideNav or TopNav for routes.',
507
+ },
508
+ {
509
+ guidance: false,
510
+ description:
511
+ 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.',
512
+ },
513
+ {
514
+ guidance: false,
515
+ description:
516
+ 'Rely on onNavigateEnd to mean "arrived" - it also fires when the user interrupts the scroll.',
517
+ },
406
518
  ],
407
519
  },
408
520
  propDescriptions: {
409
- items: 'Ordered OutlineItem[]: {id,label,level}. id should match target heading DOM id.',
521
+ items:
522
+ 'Ordered OutlineItem[]: {id,label,level}. id should match target heading DOM id.',
410
523
  activeId: 'Controlled active heading id. Disables built-in scroll-spy.',
411
524
  onActiveIdChange: 'Called when active id changes from scroll-spy or click.',
412
525
  label: "Accessible nav label. Default: 'Table of contents'.",
413
- density: "Density variant: 'default' (standard) or 'compact'. Default: 'default'.",
414
- onNavigateStart: 'Called with the id when navigation begins, before the scroll.',
415
- onNavigateEnd: 'Called with the id once the scroll settles. Fires exactly once per onNavigateStart, including on user interruption.',
416
- offset: 'Height in px of a fixed header overlaying the scroll root. Shifts BOTH the activation line and the scroll landing, so a heading activates where navigating to it lands it. Composes with (does not replace) each heading scroll-margin-top. Default: 0.',
417
- scrollContainerRef: 'RefObject of the scroll container to track, instead of the nearest scrollable ancestor. For split panes, modals, panels.',
418
- hasScrollOnClick: 'Whether activating an item smooth-scrolls to it. false = you own scrolling; active id, hash and callbacks still update. Default: true.',
526
+ density:
527
+ "Density variant: 'default' (standard) or 'compact'. Default: 'default'.",
528
+ onNavigateStart:
529
+ 'Called with the id when navigation begins, before the scroll.',
530
+ onNavigateEnd:
531
+ 'Called with the id once the scroll settles. Fires exactly once per onNavigateStart, including on user interruption.',
532
+ offset:
533
+ 'Height in px of a fixed header overlaying the scroll root. Shifts BOTH the activation line and the scroll landing, so a heading activates where navigating to it lands it. Composes with (does not replace) each heading scroll-margin-top. Default: 0.',
534
+ scrollContainerRef:
535
+ 'RefObject of the scroll container to track, instead of the nearest scrollable ancestor. For split panes, modals, panels.',
536
+ hasScrollOnClick:
537
+ 'Whether activating an item smooth-scrolls to it. false = you own scrolling; active id, hash and callbacks still update. Default: true.',
419
538
  xstyle: 'StyleX styles for layout. Must be stylex.create() value.',
420
539
  },
421
540
  components: [
@@ -423,18 +542,25 @@ export const docsDense = {
423
542
  name: 'Outline',
424
543
  displayName: 'Outline',
425
544
  description:
426
- 'Document outline nav with sliding indicator. Renders heading anchors with a density variant, roving-tabindex keyboard nav, navigate callbacks, and scroll-spy active state when uncontrolled.',
545
+ 'Document outline nav with sliding indicator. Renders heading anchors with a density variant, links in the normal Tab order, navigate callbacks, and scroll-spy active state when uncontrolled.',
427
546
  propDescriptions: {
428
- items: 'Ordered OutlineItem[]: {id,label,level}. id should match target heading DOM id.',
547
+ items:
548
+ 'Ordered OutlineItem[]: {id,label,level}. id should match target heading DOM id.',
429
549
  activeId: 'Controlled active heading id. Disables built-in scroll-spy.',
430
- onActiveIdChange: 'Called when active id changes from scroll-spy or click.',
550
+ onActiveIdChange:
551
+ 'Called when active id changes from scroll-spy or click.',
431
552
  label: "Accessible nav label. Default: 'Table of contents'.",
432
553
  density: "Density variant: 'default' | 'compact'. Default: 'default'.",
433
- onNavigateStart: 'Called with the id when navigation begins, before the scroll.',
434
- onNavigateEnd: 'Called with the id once the scroll settles. Fires exactly once per onNavigateStart, including on user interruption.',
435
- offset: 'Height in px of a fixed header overlaying the scroll root. Shifts both the activation line and the scroll landing. Composes with scroll-margin-top. Default: 0.',
436
- scrollContainerRef: 'RefObject of the scroll container to track, instead of the nearest scrollable ancestor.',
437
- hasScrollOnClick: 'Whether activating an item smooth-scrolls to it. Default: true.',
554
+ onNavigateStart:
555
+ 'Called with the id when navigation begins, before the scroll.',
556
+ onNavigateEnd:
557
+ 'Called with the id once the scroll settles. Fires exactly once per onNavigateStart, including on user interruption.',
558
+ offset:
559
+ 'Height in px of a fixed header overlaying the scroll root. Shifts both the activation line and the scroll landing. Composes with scroll-margin-top. Default: 0.',
560
+ scrollContainerRef:
561
+ 'RefObject of the scroll container to track, instead of the nearest scrollable ancestor.',
562
+ hasScrollOnClick:
563
+ 'Whether activating an item smooth-scrolls to it. Default: true.',
438
564
  xstyle: 'StyleX styles for layout. Must be stylex.create() value.',
439
565
  },
440
566
  },
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file Outline.tsx
7
- * @input Uses React, StyleX, Astryx link provider integration, Outline hooks/types, useListFocus
7
+ * @input Uses React, StyleX, Astryx link provider integration, Outline hooks/types
8
8
  * @output Exports Outline component and OutlineProps type
9
9
  * @position Core implementation; consumed by index.ts
10
10
  *
@@ -12,7 +12,7 @@
12
12
  * - Sliding indicator track (vertical divider + animated active bar)
13
13
  * - Density variant (default/compact)
14
14
  * - Scroll-spy active state when uncontrolled
15
- * - Roving-tabindex keyboard navigation (Arrow/Home/End, Enter/Space activate)
15
+ * - Each link in the normal Tab order (Enter/Space activate)
16
16
  * - onNavigateStart / onNavigateEnd callbacks around the smooth scroll
17
17
  * - Scroll scoping (offset, scrollContainerRef, hasScrollOnClick)
18
18
  *
@@ -35,8 +35,6 @@ import {
35
35
  fontWeightVars,
36
36
  } from '../theme/tokens.stylex';
37
37
  import {useLinkComponent} from '../Link/useLinkComponent';
38
- import {useListFocus} from '../hooks/useListFocus';
39
- import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
40
38
  import {mergeProps} from '../utils';
41
39
  import type {BaseProps} from '../BaseProps';
42
40
  import {useScrollSpy} from './useScrollSpy';
@@ -304,10 +302,8 @@ function moveFocusTo(id: string) {
304
302
  * scroll root) plus the heading's own `scroll-margin-top`. It defaults to the
305
303
  * first item at the top and the last at the bottom.
306
304
  *
307
- * Keyboard: the list is a single tab stop (roving tabindex), seated on the
308
- * active heading. Arrow keys move between headings, Home/End jump to the ends,
309
- * and Enter/Space activate — so a long table of contents costs one Tab press,
310
- * not one per heading.
305
+ * Keyboard: each link is in the normal Tab order, and Enter/Space activate.
306
+ * There is no arrow, Home, or End movement between links.
311
307
  *
312
308
  * @example
313
309
  * ```
@@ -369,49 +365,6 @@ export function Outline({
369
365
  onNavigateEnd,
370
366
  });
371
367
 
372
- // Roving tabindex over the links: the whole outline is one tab stop, and
373
- // arrows move between headings. The hook owns `tabindex` on the items.
374
- const {
375
- listRef,
376
- handleKeyDown: handleListKeyDown,
377
- handleFocus,
378
- } = useListFocus<HTMLUListElement>({
379
- itemSelector: 'a[href]',
380
- orientation: 'vertical',
381
- hasRovingTabIndex: true,
382
- });
383
-
384
- // Seat the tab stop on the *active* heading. WAI-ARIA puts the single tab
385
- // stop on the current item, so tabbing into a table of contents while
386
- // reading section 7 lands on section 7 — not back at section 1, which is
387
- // where useListFocus would otherwise leave it (it promotes the first item on
388
- // mount and then keeps whichever item already holds the stop).
389
- //
390
- // Only while focus is outside the list: once the reader has arrowed to an
391
- // item, the stop is theirs and scroll-spy must not yank it away. Sets
392
- // `tabindex` only — it never steals focus. Registered after useListFocus so
393
- // it runs after the hook's own layout effect and wins; on later commits the
394
- // hook then finds this item already holding the stop and keeps it.
395
- useIsomorphicLayoutEffect(() => {
396
- const list = listRef.current;
397
- if (list == null || list.contains(document.activeElement)) {
398
- return;
399
- }
400
- const links = Array.from(list.querySelectorAll<HTMLElement>('a[href]'));
401
- const active = links.find(
402
- link => link.getAttribute('aria-current') === 'location',
403
- );
404
- if (active == null) {
405
- return;
406
- }
407
- for (const link of links) {
408
- const tabIndex = link === active ? '0' : '-1';
409
- if (link.getAttribute('tabindex') !== tabIndex) {
410
- link.setAttribute('tabindex', tabIndex);
411
- }
412
- }
413
- });
414
-
415
368
  /**
416
369
  * The single navigation path, shared by click and keyboard activation:
417
370
  * push the hash, then hand off to the scroll-spy's `scrollTo`, which fires
@@ -480,12 +433,7 @@ export function Outline({
480
433
  className,
481
434
  style,
482
435
  )}>
483
- <ul
484
- {...stylex.props(styles.list)}
485
- ref={listRef}
486
- role="list"
487
- onKeyDown={handleListKeyDown}
488
- onFocus={handleFocus}>
436
+ <ul {...stylex.props(styles.list)} role="list">
489
437
  {items.map(item => {
490
438
  const isActive = item.id === resolvedActiveId;
491
439
 
@@ -0,0 +1,157 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').HookDoc} */
4
+ export const docs = {
5
+ name: 'useOutlineFromMarkdown',
6
+ displayName: 'useOutlineFromMarkdown',
7
+ keywords: [
8
+ 'outline',
9
+ 'table of contents',
10
+ 'toc',
11
+ 'markdown',
12
+ 'headings',
13
+ 'heading ids',
14
+ 'same-page navigation',
15
+ ],
16
+ params: [
17
+ {
18
+ name: 'markdown',
19
+ type: 'string',
20
+ description:
21
+ 'The Markdown source. Pass the same string you render with Markdown.',
22
+ required: true,
23
+ },
24
+ {
25
+ name: 'options',
26
+ type: 'ParseOutlineFromMarkdownOptions',
27
+ description:
28
+ 'Parse options. Omit them for a document that Markdown renders without plugins.',
29
+ required: false,
30
+ },
31
+ {
32
+ name: 'options.plugins',
33
+ type: 'ReadonlyArray<MarkdownPluginEntry>',
34
+ description:
35
+ 'The ordered Markdown plugin list. Pass the same list to Markdown, so transforms, labels and ids agree. The outline is recomputed when the list reference changes.',
36
+ required: false,
37
+ },
38
+ {
39
+ name: 'options.isFinal',
40
+ type: 'boolean',
41
+ description:
42
+ "Whether the source is complete. Pass false while Markdown's isStreaming is true, so both apply the same transforms.",
43
+ default: 'true',
44
+ required: false,
45
+ },
46
+ ],
47
+ returns: [
48
+ {
49
+ name: 'items',
50
+ type: 'OutlineItem[]',
51
+ description:
52
+ "One item per root heading, in document order, ready for Outline's items prop. id is the id Markdown renders on the heading in a complete document without sources, label is the heading's text, and level is its depth from 1 to 6.",
53
+ },
54
+ ],
55
+ usage: {
56
+ description:
57
+ 'Extracts the headings of a Markdown string for an Outline next to the rendered document. It uses the parser and heading slugs that Markdown uses, so in a complete document each item links to its heading, and fenced code never counts as a heading. The result is memoized on the source and the options. Outside React, use parseOutlineFromMarkdown.',
58
+ bestPractices: [
59
+ {
60
+ guidance: true,
61
+ description:
62
+ 'Pass the same string, plugin list and finality to Markdown and this hook, so items match the rendered headings once the content is complete.',
63
+ },
64
+ {
65
+ guidance: true,
66
+ description:
67
+ 'Declare the plugin list outside the component or memoize it. A new array on each render recomputes the outline.',
68
+ },
69
+ {
70
+ guidance: true,
71
+ description:
72
+ 'For several documents on one page, install one createMarkdownHeadingLinks per document with headingIdPrefix, so their ids do not collide.',
73
+ },
74
+ {
75
+ guidance: false,
76
+ description:
77
+ 'Write the items by hand or find headings with a regular expression. They drift from the rendered ids.',
78
+ },
79
+ {
80
+ guidance: false,
81
+ description:
82
+ 'Expect every item to have a target while content streams. Markdown reveals text gradually and trims unfinished syntax, so a heading can be absent or have another id until the content is complete.',
83
+ },
84
+ {
85
+ guidance: false,
86
+ description:
87
+ "Expect an item to link to a heading that cites a Markdown sources entry. The hook does not receive sources, so the ids differ: for '# Intro [ref]', Markdown renders intro and the hook returns intro-ref.",
88
+ },
89
+ {
90
+ guidance: false,
91
+ description:
92
+ 'Expect an item for a nested heading, such as one in a blockquote. Only root headings become items.',
93
+ },
94
+ ],
95
+ },
96
+ relatedComponents: ['Outline', 'Markdown'],
97
+ importPath: '@astryxdesign/core/Outline',
98
+ category: 'Navigation',
99
+ };
100
+
101
+ /** @type {import('@astryxdesign/cli/authoring').HookTranslationDoc} */
102
+ export const docsDense = {
103
+ description:
104
+ 'Headings of a Markdown string as OutlineItem[] for an Outline beside the rendered document. Same parser and heading slugs as Markdown; memoized on source and options.',
105
+ paramDescriptions: {
106
+ markdown: 'Markdown source; the same string you render with Markdown.',
107
+ options: 'parse options; omit for a document without plugins.',
108
+ 'options.plugins':
109
+ 'ordered plugin list; pass the same list to Markdown. New reference recomputes.',
110
+ 'options.isFinal':
111
+ "whether the source is complete; false while Markdown's isStreaming is true.",
112
+ },
113
+ returnDescriptions: {
114
+ items:
115
+ 'one item per root heading in document order: id is the rendered id in a complete document without sources, label is its text, level is 1-6.',
116
+ },
117
+ usage: {
118
+ description:
119
+ 'Headings of a Markdown string for an Outline next to the rendered document. Same parser and slugs as Markdown, so items link to their headings in a complete document. Outside React, use parseOutlineFromMarkdown.',
120
+ bestPractices: [
121
+ {
122
+ guidance: true,
123
+ description:
124
+ 'Pass the same string, plugin list and finality to Markdown and the hook; items match the headings once content is complete.',
125
+ },
126
+ {
127
+ guidance: true,
128
+ description:
129
+ 'Keep the plugin list stable; a new array each render recomputes.',
130
+ },
131
+ {
132
+ guidance: true,
133
+ description:
134
+ 'Several documents on one page: one createMarkdownHeadingLinks per document with headingIdPrefix.',
135
+ },
136
+ {
137
+ guidance: false,
138
+ description: 'Write items by hand or find headings with a regex.',
139
+ },
140
+ {
141
+ guidance: false,
142
+ description:
143
+ 'Expect every item to have a target while content streams; a heading can be absent or have another id until complete.',
144
+ },
145
+ {
146
+ guidance: false,
147
+ description:
148
+ 'Expect an item to link to a heading that cites a Markdown sources entry; the hook does not receive sources, so the ids differ.',
149
+ },
150
+ {
151
+ guidance: false,
152
+ description:
153
+ 'Expect items for nested headings; only root headings count.',
154
+ },
155
+ ],
156
+ },
157
+ };
@@ -10,6 +10,7 @@
10
10
  *
11
11
  * SYNC: When modified, update these files to stay in sync:
12
12
  * - /packages/core/src/Outline/Outline.doc.mjs
13
+ * - /packages/core/src/Outline/useOutlineFromMarkdown.doc.mjs
13
14
  * - /packages/core/src/Outline/index.ts
14
15
  */
15
16
 
@@ -410,9 +410,9 @@ export function useScrollSpy({
410
410
  const onSettle = () => finish(true);
411
411
  const onManual = () => finish(false);
412
412
  const onKeyDown = (event: KeyboardEvent) => {
413
- // A key the outline itself consumed (arrow roving focus, Space
414
- // activation) is prevented, so the browser will not scroll — it is not a
415
- // manual scroll intent and must not cancel the navigation it just began.
413
+ // The outline prevents the Space key that activates a link. The
414
+ // browser then does not scroll. So this key is not a manual scroll, and
415
+ // it does not end the navigation.
416
416
  if (!event.defaultPrevented && SCROLL_KEYS.has(event.key)) {
417
417
  finish(false);
418
418
  }
@@ -65,7 +65,7 @@ export const docs = {
65
65
  {
66
66
  name: 'hasClear',
67
67
  type: 'boolean',
68
- description: 'Show a clear-all button for removing all filters.',
68
+ description: 'Show a clear-all button that removes every editable filter. Read-only filters stay.',
69
69
  default: 'true',
70
70
  },
71
71
  {
@@ -336,7 +336,7 @@ export const docsZh = {
336
336
  {
337
337
  name: 'hasClear',
338
338
  type: 'boolean',
339
- description: '显示清除全部按钮以移除所有过滤器。',
339
+ description: '显示清除全部按钮,移除可编辑的过滤器,保留只读过滤器。',
340
340
  default: 'true',
341
341
  },
342
342
  {
@@ -507,7 +507,7 @@ export const docsDense = {
507
507
  isLabelHidden: 'Visually hides label while keeping accessible.',
508
508
  placeholder: 'Text shown when no filters selected.',
509
509
  hasAutoFocus: 'Auto-focus input on mount.',
510
- hasClear: 'Show clear-all button for removing all filters.',
510
+ hasClear: 'Show clear-all button; removes editable filters, keeps read-only ones.',
511
511
  isReadOnly: 'Prevent adding, editing, or removing filters.',
512
512
  isDisabled: 'Disables entire component.',
513
513
  status: 'Validation status object w/ type + optional message.',