@lar-works/design-system 0.2.1 → 0.3.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.
Files changed (432) hide show
  1. package/README.md +5 -1
  2. package/dist/actions/Button.d.ts +39 -1
  3. package/dist/actions/Button.js +39 -9
  4. package/dist/actions/Button.js.map +1 -1
  5. package/dist/actions/ButtonLink.d.ts +5 -1
  6. package/dist/actions/ButtonLink.js +28 -2
  7. package/dist/actions/ButtonLink.js.map +1 -1
  8. package/dist/actions/ButtonRecipes.js +7 -0
  9. package/dist/actions/ButtonRecipes.js.map +1 -0
  10. package/dist/actions/IconButton.d.ts +2 -2
  11. package/dist/actions/IconButton.js +1 -1
  12. package/dist/actions/IconButton.js.map +1 -1
  13. package/dist/actions/ThemeSwitch.js +1 -1
  14. package/dist/board/ActivityLine.d.ts +36 -0
  15. package/dist/board/ActivityLine.js +144 -0
  16. package/dist/board/ActivityLine.js.map +1 -0
  17. package/dist/board/ActivityTrack.d.ts +11 -0
  18. package/dist/board/ActivityTrack.js +25 -0
  19. package/dist/board/ActivityTrack.js.map +1 -0
  20. package/dist/board/AddItem.d.ts +82 -0
  21. package/dist/board/AddItem.js +185 -0
  22. package/dist/board/AddItem.js.map +1 -0
  23. package/dist/board/Archive.d.ts +21 -0
  24. package/dist/board/Archive.js +63 -0
  25. package/dist/board/Archive.js.map +1 -0
  26. package/dist/board/Attention.d.ts +66 -0
  27. package/dist/board/Attention.js +122 -0
  28. package/dist/board/Attention.js.map +1 -0
  29. package/dist/board/BoardShell.d.ts +16 -0
  30. package/dist/board/BoardShell.js +35 -0
  31. package/dist/board/BoardShell.js.map +1 -0
  32. package/dist/board/BoardTitleBar.d.ts +50 -0
  33. package/dist/board/BoardTitleBar.js +125 -0
  34. package/dist/board/BoardTitleBar.js.map +1 -0
  35. package/dist/board/ButtonKinds.d.ts +24 -0
  36. package/dist/board/ButtonKinds.js +19 -0
  37. package/dist/board/ButtonKinds.js.map +1 -0
  38. package/dist/board/Cleanup.d.ts +8 -0
  39. package/dist/board/Cleanup.js +15 -0
  40. package/dist/board/Cleanup.js.map +1 -0
  41. package/dist/board/Counters.d.ts +101 -0
  42. package/dist/board/Counters.js +202 -0
  43. package/dist/board/Counters.js.map +1 -0
  44. package/dist/board/DataAttributes.d.ts +6 -0
  45. package/dist/board/Dependencies.d.ts +17 -0
  46. package/dist/board/Dependencies.js +44 -0
  47. package/dist/board/Dependencies.js.map +1 -0
  48. package/dist/board/Desk.d.ts +52 -0
  49. package/dist/board/Desk.js +130 -0
  50. package/dist/board/Desk.js.map +1 -0
  51. package/dist/board/Dossier.d.ts +100 -0
  52. package/dist/board/Dossier.js +194 -0
  53. package/dist/board/Dossier.js.map +1 -0
  54. package/dist/board/Filters.d.ts +33 -0
  55. package/dist/board/Filters.js +64 -0
  56. package/dist/board/Filters.js.map +1 -0
  57. package/dist/board/ItemDetails.d.ts +167 -0
  58. package/dist/board/ItemDetails.js +339 -0
  59. package/dist/board/ItemDetails.js.map +1 -0
  60. package/dist/board/ItemDialog.d.ts +73 -0
  61. package/dist/board/ItemDialog.js +140 -0
  62. package/dist/board/ItemDialog.js.map +1 -0
  63. package/dist/board/ItemParts.d.ts +42 -0
  64. package/dist/board/ItemParts.js +82 -0
  65. package/dist/board/ItemParts.js.map +1 -0
  66. package/dist/board/ItemProgress.d.ts +56 -0
  67. package/dist/board/ItemProgress.js +138 -0
  68. package/dist/board/ItemProgress.js.map +1 -0
  69. package/dist/board/List.d.ts +65 -0
  70. package/dist/board/List.js +118 -0
  71. package/dist/board/List.js.map +1 -0
  72. package/dist/board/LoadingScreen.d.ts +38 -0
  73. package/dist/board/LoadingScreen.js +74 -0
  74. package/dist/board/LoadingScreen.js.map +1 -0
  75. package/dist/board/NeedsYou.d.ts +13 -0
  76. package/dist/board/NeedsYou.js +31 -0
  77. package/dist/board/NeedsYou.js.map +1 -0
  78. package/dist/board/Onboarding.d.ts +53 -0
  79. package/dist/board/Onboarding.js +122 -0
  80. package/dist/board/Onboarding.js.map +1 -0
  81. package/dist/board/PartsList.d.ts +60 -0
  82. package/dist/board/PartsList.js +115 -0
  83. package/dist/board/PartsList.js.map +1 -0
  84. package/dist/board/ProjectIcon.d.ts +51 -0
  85. package/dist/board/ProjectIcon.js +120 -0
  86. package/dist/board/ProjectIcon.js.map +1 -0
  87. package/dist/board/PullRequests.d.ts +43 -0
  88. package/dist/board/PullRequests.js +83 -0
  89. package/dist/board/PullRequests.js.map +1 -0
  90. package/dist/board/RowStyles.js +14 -0
  91. package/dist/board/RowStyles.js.map +1 -0
  92. package/dist/board/Settings.d.ts +54 -0
  93. package/dist/board/Settings.js +112 -0
  94. package/dist/board/Settings.js.map +1 -0
  95. package/dist/board/Slices.d.ts +32 -0
  96. package/dist/board/Slices.js +88 -0
  97. package/dist/board/Slices.js.map +1 -0
  98. package/dist/board/SourceIcon.d.ts +12 -0
  99. package/dist/board/SourceIcon.js +24 -0
  100. package/dist/board/SourceIcon.js.map +1 -0
  101. package/dist/board/StagePane.d.ts +43 -0
  102. package/dist/board/StagePane.js +97 -0
  103. package/dist/board/StagePane.js.map +1 -0
  104. package/dist/board/StageRow.d.ts +141 -0
  105. package/dist/board/StageRow.js +287 -0
  106. package/dist/board/StageRow.js.map +1 -0
  107. package/dist/board/StageTrack.d.ts +18 -0
  108. package/dist/board/StageTrack.js +38 -0
  109. package/dist/board/StageTrack.js.map +1 -0
  110. package/dist/board/StartControls.d.ts +30 -0
  111. package/dist/board/StartControls.js +51 -0
  112. package/dist/board/StartControls.js.map +1 -0
  113. package/dist/board/StatusColors.d.ts +5 -0
  114. package/dist/board/StatusColors.js +17 -0
  115. package/dist/board/StatusColors.js.map +1 -0
  116. package/dist/board/StatusGlyph.d.ts +18 -0
  117. package/dist/board/StatusGlyph.js +51 -0
  118. package/dist/board/StatusGlyph.js.map +1 -0
  119. package/dist/board/Threads.d.ts +13 -0
  120. package/dist/board/Threads.js +35 -0
  121. package/dist/board/Threads.js.map +1 -0
  122. package/dist/board/TrackerLabels.d.ts +27 -0
  123. package/dist/board/TrackerLabels.js +56 -0
  124. package/dist/board/TrackerLabels.js.map +1 -0
  125. package/dist/board/UpdatePill.d.ts +52 -0
  126. package/dist/board/UpdatePill.js +188 -0
  127. package/dist/board/UpdatePill.js.map +1 -0
  128. package/dist/board/Vocabulary.d.ts +56 -0
  129. package/dist/board/Vocabulary.js +104 -0
  130. package/dist/board/Vocabulary.js.map +1 -0
  131. package/dist/board/Warnings.d.ts +35 -0
  132. package/dist/board/Warnings.js +85 -0
  133. package/dist/board/Warnings.js.map +1 -0
  134. package/dist/board/WorkMap.d.ts +136 -0
  135. package/dist/board/WorkMap.js +274 -0
  136. package/dist/board/WorkMap.js.map +1 -0
  137. package/dist/board/index.d.ts +42 -0
  138. package/dist/collections/CollectionState.d.ts +10 -1
  139. package/dist/collections/CollectionState.js +12 -5
  140. package/dist/collections/CollectionState.js.map +1 -1
  141. package/dist/collections/ResultList.js +1 -1
  142. package/dist/collections/ScrollRow.js +1 -1
  143. package/dist/collections/VirtualList.js +1 -1
  144. package/dist/display/Avatar.d.ts +5 -2
  145. package/dist/display/Avatar.js +2 -2
  146. package/dist/display/Avatar.js.map +1 -1
  147. package/dist/display/Badge.d.ts +6 -1
  148. package/dist/display/Badge.js +9 -4
  149. package/dist/display/Badge.js.map +1 -1
  150. package/dist/display/CheckList.js +1 -1
  151. package/dist/display/Comment.d.ts +12 -0
  152. package/dist/display/Comment.js +39 -0
  153. package/dist/display/Comment.js.map +1 -0
  154. package/dist/display/CopyButton.d.ts +10 -2
  155. package/dist/display/CopyButton.js +29 -16
  156. package/dist/display/CopyButton.js.map +1 -1
  157. package/dist/display/Diagram.d.ts +17 -0
  158. package/dist/display/Diagram.js +49 -0
  159. package/dist/display/Diagram.js.map +1 -0
  160. package/dist/display/ImagePicker.js +1 -1
  161. package/dist/display/InfoBox.js +2 -2
  162. package/dist/display/InsetPanel.d.ts +3 -1
  163. package/dist/display/InsetPanel.js +7 -4
  164. package/dist/display/InsetPanel.js.map +1 -1
  165. package/dist/display/ItemAction.js +2 -2
  166. package/dist/display/LabelChip.d.ts +26 -0
  167. package/dist/display/LabelChip.js +41 -0
  168. package/dist/display/LabelChip.js.map +1 -0
  169. package/dist/display/LabelTint.js +60 -0
  170. package/dist/display/LabelTint.js.map +1 -0
  171. package/dist/display/Person.d.ts +5 -0
  172. package/dist/display/Person.js +19 -0
  173. package/dist/display/Person.js.map +1 -0
  174. package/dist/display/PropertySheet.js +1 -1
  175. package/dist/display/RadioChoice.d.ts +9 -2
  176. package/dist/display/RadioChoice.js +5 -1
  177. package/dist/display/RadioChoice.js.map +1 -1
  178. package/dist/display/RatingLink.js +1 -1
  179. package/dist/display/RichText.d.ts +12 -0
  180. package/dist/display/RichText.js +94 -0
  181. package/dist/display/RichText.js.map +1 -0
  182. package/dist/display/RowList.js +1 -1
  183. package/dist/display/index.d.ts +6 -1
  184. package/dist/feedback/Alert.js +1 -1
  185. package/dist/feedback/AsyncState.js +1 -1
  186. package/dist/feedback/DismissButton.d.ts +12 -0
  187. package/dist/feedback/DismissButton.js +20 -0
  188. package/dist/feedback/DismissButton.js.map +1 -0
  189. package/dist/feedback/ErrorNotification.js +2 -0
  190. package/dist/feedback/ErrorNotification.js.map +1 -1
  191. package/dist/feedback/FormResult.d.ts +16 -0
  192. package/dist/feedback/FormResult.js +30 -0
  193. package/dist/feedback/FormResult.js.map +1 -0
  194. package/dist/feedback/LiveRegion.d.ts +7 -0
  195. package/dist/feedback/LiveRegion.js +15 -0
  196. package/dist/feedback/LiveRegion.js.map +1 -0
  197. package/dist/feedback/Loading.d.ts +19 -0
  198. package/dist/feedback/Loading.js +63 -0
  199. package/dist/feedback/Loading.js.map +1 -0
  200. package/dist/feedback/Notifications.d.ts +35 -1
  201. package/dist/feedback/Notifications.js +196 -4
  202. package/dist/feedback/Notifications.js.map +1 -1
  203. package/dist/feedback/Progress.d.ts +15 -0
  204. package/dist/feedback/Progress.js +38 -0
  205. package/dist/feedback/Progress.js.map +1 -0
  206. package/dist/feedback/ProgressStatusRow.js +1 -1
  207. package/dist/feedback/StatusIndicator.d.ts +6 -1
  208. package/dist/feedback/StatusIndicator.js +19 -5
  209. package/dist/feedback/StatusIndicator.js.map +1 -1
  210. package/dist/feedback/index.d.ts +8 -4
  211. package/dist/forms/Checkbox.js +1 -1
  212. package/dist/forms/ColorSwatches.d.ts +21 -0
  213. package/dist/forms/ColorSwatches.js +26 -0
  214. package/dist/forms/ColorSwatches.js.map +1 -0
  215. package/dist/forms/FormSection.d.ts +68 -6
  216. package/dist/forms/FormSection.js +139 -8
  217. package/dist/forms/FormSection.js.map +1 -1
  218. package/dist/forms/HiddenField.d.ts +6 -0
  219. package/dist/forms/HiddenField.js +13 -0
  220. package/dist/forms/HiddenField.js.map +1 -0
  221. package/dist/forms/Input.d.ts +12 -2
  222. package/dist/forms/Input.js +10 -3
  223. package/dist/forms/Input.js.map +1 -1
  224. package/dist/forms/LabelPicker.d.ts +43 -0
  225. package/dist/forms/LabelPicker.js +107 -0
  226. package/dist/forms/LabelPicker.js.map +1 -0
  227. package/dist/forms/MarkdownEditor.d.ts +21 -0
  228. package/dist/forms/MarkdownEditor.js +175 -0
  229. package/dist/forms/MarkdownEditor.js.map +1 -0
  230. package/dist/forms/MarkdownToolbar.js +204 -0
  231. package/dist/forms/MarkdownToolbar.js.map +1 -0
  232. package/dist/forms/OptionSelect.d.ts +30 -9
  233. package/dist/forms/OptionSelect.js +53 -13
  234. package/dist/forms/OptionSelect.js.map +1 -1
  235. package/dist/forms/Select.d.ts +4 -1
  236. package/dist/forms/Select.js +3 -1
  237. package/dist/forms/Select.js.map +1 -1
  238. package/dist/forms/Switch.d.ts +4 -1
  239. package/dist/forms/Switch.js +5 -2
  240. package/dist/forms/Switch.js.map +1 -1
  241. package/dist/forms/TagInput.js +1 -1
  242. package/dist/forms/index.d.ts +7 -3
  243. package/dist/foundations/AmbientMotion.d.ts +2 -0
  244. package/dist/foundations/AmbientMotion.js +39 -0
  245. package/dist/foundations/AmbientMotion.js.map +1 -0
  246. package/dist/foundations/DesignSystemProvider.d.ts +34 -0
  247. package/dist/foundations/DesignSystemProvider.js +26 -1
  248. package/dist/foundations/DesignSystemProvider.js.map +1 -1
  249. package/dist/foundations/index.d.ts +2 -1
  250. package/dist/icons/BrandIcons.d.ts +8 -2
  251. package/dist/icons/BrandIcons.js +14 -3
  252. package/dist/icons/BrandIcons.js.map +1 -1
  253. package/dist/icons/dynamic.d.ts +2 -0
  254. package/dist/icons/dynamic.js +1 -0
  255. package/dist/icons/index.d.ts +2 -2
  256. package/dist/icons/index.js +1 -1
  257. package/dist/icons.d.ts +2 -2
  258. package/dist/icons.js +1 -1
  259. package/dist/index.d.ts +71 -10
  260. package/dist/index.js +78 -21
  261. package/dist/layout/Site.js +1 -1
  262. package/dist/layout/TitleBar.js +1 -1
  263. package/dist/layout/index.d.ts +2 -2
  264. package/dist/layout/index.js +3 -3
  265. package/dist/layout/toolbar/CommandPaletteTrigger.d.ts +17 -2
  266. package/dist/layout/toolbar/CommandPaletteTrigger.js +89 -6
  267. package/dist/layout/toolbar/CommandPaletteTrigger.js.map +1 -1
  268. package/dist/layout/toolbar/TriggerFit.js +56 -0
  269. package/dist/layout/toolbar/TriggerFit.js.map +1 -0
  270. package/dist/navigation/CollectionBrowseSelector.js +1 -1
  271. package/dist/navigation/CollectionViewToggle.d.ts +26 -0
  272. package/dist/navigation/CollectionViewToggle.js +54 -23
  273. package/dist/navigation/CollectionViewToggle.js.map +1 -1
  274. package/dist/navigation/DrilldownBrowseSelector.js +1 -1
  275. package/dist/navigation/FolderSelector.js +2 -2
  276. package/dist/navigation/SortMenu.d.ts +10 -0
  277. package/dist/navigation/SortMenu.js +33 -0
  278. package/dist/navigation/SortMenu.js.map +1 -0
  279. package/dist/navigation/index.d.ts +3 -2
  280. package/dist/overlays/CommandPalette.d.ts +38 -4
  281. package/dist/overlays/CommandPalette.js +140 -54
  282. package/dist/overlays/CommandPalette.js.map +1 -1
  283. package/dist/overlays/Drawer.js +1 -1
  284. package/dist/overlays/DropdownMenu.d.ts +7 -1
  285. package/dist/overlays/DropdownMenu.js +13 -2
  286. package/dist/overlays/DropdownMenu.js.map +1 -1
  287. package/dist/overlays/HelpTip.d.ts +8 -0
  288. package/dist/overlays/HelpTip.js +29 -0
  289. package/dist/overlays/HelpTip.js.map +1 -0
  290. package/dist/overlays/ImageViewer.js +2 -2
  291. package/dist/overlays/Modal.d.ts +5 -0
  292. package/dist/overlays/Modal.js +17 -2
  293. package/dist/overlays/Modal.js.map +1 -1
  294. package/dist/overlays/ModalExpandButton.d.ts +18 -0
  295. package/dist/overlays/ModalExpandButton.js +43 -0
  296. package/dist/overlays/ModalExpandButton.js.map +1 -0
  297. package/dist/overlays/Popover.d.ts +2 -1
  298. package/dist/overlays/Popover.js +1 -0
  299. package/dist/overlays/Popover.js.map +1 -1
  300. package/dist/overlays/index.d.ts +4 -2
  301. package/dist/showcase/Cards.js +1 -1
  302. package/dist/showcase/FeatureRow.js +1 -1
  303. package/dist/showcase/PhotoSearchConcept.js +1 -1
  304. package/dist/showcase/ProductBand.js +1 -1
  305. package/dist/showcase/Status.js +1 -1
  306. package/dist/typography/Code.d.ts +18 -0
  307. package/dist/typography/Code.js +24 -0
  308. package/dist/typography/Code.js.map +1 -0
  309. package/dist/typography/Link.d.ts +7 -2
  310. package/dist/typography/Link.js +15 -5
  311. package/dist/typography/Link.js.map +1 -1
  312. package/dist/typography/Typography.d.ts +19 -1
  313. package/dist/typography/Typography.js +11 -3
  314. package/dist/typography/Typography.js.map +1 -1
  315. package/dist/typography/index.d.ts +3 -3
  316. package/package.json +26 -3
  317. package/src/actions/Button.tsx +78 -11
  318. package/src/actions/ButtonLink.tsx +34 -0
  319. package/src/actions/ButtonRecipes.ts +10 -0
  320. package/src/actions/IconButton.tsx +7 -4
  321. package/src/board/ActivityLine.tsx +176 -0
  322. package/src/board/ActivityTrack.tsx +23 -0
  323. package/src/board/AddItem.tsx +259 -0
  324. package/src/board/Archive.tsx +75 -0
  325. package/src/board/Attention.tsx +135 -0
  326. package/src/board/BoardShell.tsx +30 -0
  327. package/src/board/BoardTitleBar.tsx +167 -0
  328. package/src/board/ButtonKinds.ts +32 -0
  329. package/src/board/Cleanup.tsx +19 -0
  330. package/src/board/Counters.tsx +224 -0
  331. package/src/board/DataAttributes.ts +3 -0
  332. package/src/board/Dependencies.tsx +49 -0
  333. package/src/board/Desk.tsx +155 -0
  334. package/src/board/Dossier.tsx +262 -0
  335. package/src/board/Filters.tsx +85 -0
  336. package/src/board/ItemDetails.tsx +435 -0
  337. package/src/board/ItemDialog.tsx +179 -0
  338. package/src/board/ItemParts.tsx +128 -0
  339. package/src/board/ItemProgress.tsx +164 -0
  340. package/src/board/List.tsx +154 -0
  341. package/src/board/LoadingScreen.tsx +78 -0
  342. package/src/board/NeedsYou.tsx +30 -0
  343. package/src/board/Onboarding.tsx +124 -0
  344. package/src/board/PartsList.tsx +160 -0
  345. package/src/board/ProjectIcon.tsx +172 -0
  346. package/src/board/PullRequests.tsx +106 -0
  347. package/src/board/RowStyles.ts +17 -0
  348. package/src/board/Settings.tsx +124 -0
  349. package/src/board/Slices.tsx +103 -0
  350. package/src/board/SourceIcon.tsx +33 -0
  351. package/src/board/StagePane.tsx +128 -0
  352. package/src/board/StageRow.tsx +320 -0
  353. package/src/board/StageTrack.tsx +61 -0
  354. package/src/board/StartControls.tsx +77 -0
  355. package/src/board/StatusColors.ts +28 -0
  356. package/src/board/StatusGlyph.tsx +70 -0
  357. package/src/board/Threads.tsx +38 -0
  358. package/src/board/TrackerLabels.tsx +75 -0
  359. package/src/board/UpdatePill.tsx +222 -0
  360. package/src/board/Vocabulary.tsx +155 -0
  361. package/src/board/Warnings.tsx +83 -0
  362. package/src/board/WorkMap.tsx +436 -0
  363. package/src/board/base.css +121 -0
  364. package/src/board/button-kinds.css +8 -0
  365. package/src/board/index.ts +224 -0
  366. package/src/board/items.css +75 -0
  367. package/src/board/loading-screen.css +171 -0
  368. package/src/board/rows.css +180 -0
  369. package/src/board/title-bar.css +56 -0
  370. package/src/board/tokens.css +52 -0
  371. package/src/board.css +19 -0
  372. package/src/collections/CollectionState.tsx +20 -7
  373. package/src/display/Avatar.tsx +6 -3
  374. package/src/display/Badge.tsx +25 -3
  375. package/src/display/Comment.tsx +26 -0
  376. package/src/display/CopyButton.tsx +48 -18
  377. package/src/display/Diagram.tsx +59 -0
  378. package/src/display/InsetPanel.tsx +4 -1
  379. package/src/display/LabelChip.tsx +73 -0
  380. package/src/display/LabelTint.ts +72 -0
  381. package/src/display/Person.tsx +11 -0
  382. package/src/display/RadioChoice.tsx +11 -2
  383. package/src/display/RichText.tsx +114 -0
  384. package/src/display/index.ts +5 -0
  385. package/src/feedback/DismissButton.tsx +28 -0
  386. package/src/feedback/ErrorNotification.ts +3 -0
  387. package/src/feedback/FormResult.tsx +50 -0
  388. package/src/feedback/LiveRegion.tsx +10 -0
  389. package/src/feedback/Loading.tsx +75 -0
  390. package/src/feedback/Notifications.tsx +267 -2
  391. package/src/feedback/Progress.tsx +44 -0
  392. package/src/feedback/StatusIndicator.tsx +34 -3
  393. package/src/feedback/index.ts +12 -2
  394. package/src/forms/ColorSwatches.tsx +50 -0
  395. package/src/forms/FormSection.tsx +245 -10
  396. package/src/forms/HiddenField.tsx +4 -0
  397. package/src/forms/Input.tsx +26 -4
  398. package/src/forms/LabelPicker.tsx +140 -0
  399. package/src/forms/MarkdownEditor.tsx +283 -0
  400. package/src/forms/MarkdownToolbar.tsx +267 -0
  401. package/src/forms/OptionSelect.tsx +110 -25
  402. package/src/forms/Select.tsx +20 -13
  403. package/src/forms/Switch.tsx +16 -2
  404. package/src/forms/index.ts +16 -2
  405. package/src/foundations/AmbientMotion.ts +51 -0
  406. package/src/foundations/DesignSystemProvider.tsx +19 -0
  407. package/src/foundations/index.ts +1 -0
  408. package/src/foundations/theme.css +64 -0
  409. package/src/icons/BrandIcons.tsx +12 -3
  410. package/src/icons/dynamic.ts +4 -0
  411. package/src/icons/index.ts +1 -1
  412. package/src/index.ts +2 -1
  413. package/src/layout/index.ts +1 -1
  414. package/src/layout/toolbar/CommandPaletteTrigger.tsx +187 -31
  415. package/src/layout/toolbar/TriggerFit.ts +84 -0
  416. package/src/navigation/CollectionViewToggle.tsx +94 -24
  417. package/src/navigation/SortMenu.tsx +41 -0
  418. package/src/navigation/index.ts +2 -1
  419. package/src/overlays/CommandPalette.tsx +190 -60
  420. package/src/overlays/DropdownMenu.tsx +12 -1
  421. package/src/overlays/HelpTip.tsx +29 -0
  422. package/src/overlays/Modal.tsx +23 -0
  423. package/src/overlays/ModalExpandButton.tsx +48 -0
  424. package/src/overlays/Popover.tsx +3 -1
  425. package/src/overlays/index.ts +9 -1
  426. package/src/styles.css +3 -1
  427. package/src/typography/Code.tsx +24 -0
  428. package/src/typography/Link.tsx +19 -10
  429. package/src/typography/Typography.tsx +25 -2
  430. package/src/typography/index.ts +1 -1
  431. package/dist/feedback/index.js +0 -11
  432. package/dist/typography/index.js +0 -4
@@ -1,7 +1,7 @@
1
1
  /* oxlint-disable react/only-export-components -- The option hook belongs to the palette. */
2
2
 
3
3
  import { Modal as NinnaModal } from '@ninna-ui/overlays';
4
- import { SearchIcon, XIcon } from 'lucide-react';
4
+ import { CheckIcon, SearchIcon, XIcon } from 'lucide-react';
5
5
  import { createContext, type ReactNode, useContext, useEffect, useId, useRef, useState } from 'react';
6
6
 
7
7
  import { IconButton } from '../actions/IconButton';
@@ -11,6 +11,16 @@ import { Text } from '../typography/Typography';
11
11
  import { useModalAutoFocus } from './useModalAutoFocus';
12
12
  import { useScrollFade } from './useScrollFade';
13
13
 
14
+ /** A chip before the field for something that narrows the list, such as a filter. */
15
+ export interface CommandPaletteToken {
16
+ id: string;
17
+ label: string;
18
+ /** An icon or colour mark before the label. */
19
+ leading?: ReactNode;
20
+ /** A button on the chip that takes it away; the focus stays in the field. */
21
+ remove?: { label: string; onRemove: () => void };
22
+ }
23
+
14
24
  interface CommandPaletteProps {
15
25
  /** The dialog's accessible title and description; the field says the rest. */
16
26
  label: string;
@@ -20,13 +30,23 @@ interface CommandPaletteProps {
20
30
  onQueryChange: (query: string) => void;
21
31
  /** Narrows the list to one level, shown as a chip before the field; Backspace in an empty field leaves it. */
22
32
  scope?: { label: string; leaveLabel: string; onLeave: () => void };
33
+ /** Chips before the field, such as active filters, which wrap and scroll on their own when there are many. A
34
+ * palette given tokens, even none, is a filtering search, as wide as its chips need. */
35
+ tokens?: readonly CommandPaletteToken[];
36
+ /** Backspace in an empty field, such as to take the last token away. */
37
+ onBackspaceEmpty?: () => void;
38
+ /** A control after the field, such as Clear. A press on it leaves the focus in the field. */
39
+ trailing?: ReactNode;
23
40
  /** The options' values in list order, for the arrow keys and Enter. */
24
41
  options: readonly string[];
25
- onSelect: (value: string) => void;
42
+ /** May return the option the highlight moves to, when choosing changes the list. */
43
+ onSelect: (value: string) => string | void;
26
44
  onClose: () => void;
27
45
  listLabel: string;
46
+ /** Marks the list busy while more may arrive; `busyLabel` shows under it meanwhile. */
28
47
  busy?: boolean;
29
- /** Shown when nothing is listed. */
48
+ busyLabel?: ReactNode;
49
+ /** Shown when nothing is listed and nothing is pending. */
30
50
  empty?: ReactNode;
31
51
  /** Quiet key hints under the list, on screens with a keyboard. */
32
52
  footer?: ReactNode;
@@ -37,10 +57,12 @@ const OptionContext = createContext<{
37
57
  listId: string;
38
58
  activeValue: string | undefined;
39
59
  setActiveValue: (value: string) => void;
60
+ select: (value: string) => void;
40
61
  } | null>(null);
41
62
 
42
63
  /** T3 Code's command palette: a field over one keyboard list of grouped options, as a dialog over the page
43
- * or, on phones, the whole screen. The arrow keys move through `options`, Enter selects, Escape closes. */
64
+ * or, on phones, the whole screen. The arrow keys move through `options`, Enter selects, Escape closes. Tokens
65
+ * before the field say what narrows the list. */
44
66
  export function CommandPalette({
45
67
  label,
46
68
  description,
@@ -48,11 +70,15 @@ export function CommandPalette({
48
70
  query,
49
71
  onQueryChange,
50
72
  scope,
73
+ tokens: givenTokens,
74
+ onBackspaceEmpty,
75
+ trailing,
51
76
  options,
52
77
  onSelect,
53
78
  onClose,
54
79
  listLabel,
55
80
  busy = false,
81
+ busyLabel,
56
82
  empty,
57
83
  footer,
58
84
  children,
@@ -65,6 +91,19 @@ export function CommandPalette({
65
91
  const [chosen, setActiveValue] = useState<string>();
66
92
  // The highlight follows the list: when the chosen option leaves it, the first option takes over.
67
93
  const activeValue = chosen !== undefined && options.includes(chosen) ? chosen : options[0];
94
+ const select = (value: string) => {
95
+ const next = onSelect(value);
96
+
97
+ if (next) setActiveValue(next);
98
+ };
99
+ // A scope is the one token of a palette that narrows by levels.
100
+ const tokens: readonly CommandPaletteToken[] = scope
101
+ ? [{ id: 'scope', label: scope.label, remove: { label: scope.leaveLabel, onRemove: scope.onLeave } }]
102
+ : (givenTokens ?? []);
103
+ const leaveLast = scope ? scope.onLeave : onBackspaceEmpty;
104
+ // A palette that takes tokens, or a trailing control, is a filtering search: wide, with its field wrapping onto
105
+ // rows under the tokens, from the top.
106
+ const rows = givenTokens !== undefined || Boolean(trailing);
68
107
 
69
108
  useEffect(() => {
70
109
  if (activeValue) document.getElementById(optionId(listId, activeValue))?.scrollIntoView({ block: 'nearest' });
@@ -77,75 +116,115 @@ export function CommandPalette({
77
116
  description={description}
78
117
  centered={false}
79
118
  {...focusProps}
80
- className="flex max-h-[min(36rem,calc(100dvh-6rem))] w-[calc(100vw-2rem)] max-w-2xl flex-col overflow-hidden p-0 shadow-(--lar-shadow-dialog) max-sm:top-0 max-sm:left-0 max-sm:h-dvh max-sm:max-h-none max-sm:w-full max-sm:max-w-none max-sm:translate-x-0 max-sm:rounded-none! max-sm:border-0 max-sm:pt-[env(safe-area-inset-top)]"
119
+ data-command-palette
120
+ // The dialog listens for Escape on the document, past the field's own keys; an input method's Escape
121
+ // only ends its composition.
122
+ onEscapeKeyDown={(event) => {
123
+ if (event.isComposing) event.preventDefault();
124
+ }}
125
+ className={`flex max-h-[min(36rem,calc(100dvh-6rem))] w-[calc(100vw-2rem)] max-w-2xl flex-col overflow-hidden p-0 shadow-(--lar-shadow-dialog) max-sm:top-0 max-sm:left-0 max-sm:h-dvh max-sm:max-h-none max-sm:w-full max-sm:max-w-none max-sm:translate-x-0 max-sm:rounded-none! max-sm:border-0 max-sm:pt-[env(safe-area-inset-top)] ${rows ? 'max-sm:pb-[env(safe-area-inset-bottom)] sm:w-[calc(100vw-2rem)] sm:max-w-2xl' : ''}`}
81
126
  >
82
- <div className="border-base-300 [&>svg]:text-base-600 flex shrink-0 items-center gap-2 border-b px-4 py-3 [&>svg]:size-4 [&>svg]:shrink-0">
127
+ <div
128
+ className={`border-base-300 [&>svg]:text-base-600 flex shrink-0 gap-2 border-b px-4 [&>svg]:size-4 [&>svg]:shrink-0 ${rows ? 'items-start py-2 [&>svg]:mt-2' : 'items-center py-3'}`}
129
+ >
83
130
  <SearchIcon />
84
- {scope ? (
85
- <span className="bg-primary/12 text-primary flex shrink-0 items-center gap-1 rounded-md py-0.5 pr-1 pl-2 text-xs font-medium">
86
- {scope.label}
87
- <button
88
- type="button"
89
- tabIndex={-1}
90
- aria-label={scope.leaveLabel}
91
- className="hover:bg-primary/15 rounded-sm p-0.5 [&>svg]:size-3"
92
- onClick={() => {
93
- scope.onLeave();
94
- input.current?.focus();
95
- }}
131
+ <div className={`flex min-w-0 flex-1 items-center gap-x-2 ${rows ? 'flex-wrap gap-y-1' : ''}`}>
132
+ {tokens.length > 0 ? (
133
+ // Many tokens scroll on their own, so the field stays in view however many there are. Under
134
+ // a coarse pointer each remove button is 44 px, reaching into the gap between rows.
135
+ <ul
136
+ className={`flex max-w-full min-w-0 shrink-0 items-center gap-1 ${rows ? 'max-h-19 flex-wrap overflow-y-auto overscroll-contain py-1 pointer-coarse:max-h-22 pointer-coarse:gap-y-3 pointer-coarse:py-1.5' : ''}`}
96
137
  >
97
- <XIcon />
98
- </button>
99
- </span>
138
+ {tokens.map((token) => (
139
+ <li
140
+ key={token.id}
141
+ className={`bg-primary/12 text-primary flex max-w-full min-w-0 shrink-0 items-center gap-1 rounded-md py-0.5 pr-1 pl-2 text-xs font-medium ${rows ? 'dark:text-base-content pointer-coarse:min-h-8 pointer-coarse:py-0 pointer-coarse:pr-0' : ''}`}
142
+ >
143
+ {token.leading ? (
144
+ <span className="flex shrink-0 items-center [&>svg]:size-3.5">
145
+ {token.leading}
146
+ </span>
147
+ ) : null}
148
+ <span className="min-w-0 truncate">{token.label}</span>
149
+ {token.remove ? (
150
+ <button
151
+ type="button"
152
+ // A scope's chip leaves with Backspace alone; several tokens' buttons are tab stops.
153
+ tabIndex={scope ? -1 : undefined}
154
+ aria-label={token.remove.label}
155
+ className={`hover:bg-primary/15 grid shrink-0 place-items-center rounded-sm p-0.5 [&>svg]:size-3 ${rows ? 'pointer-coarse:-my-1.5 pointer-coarse:size-11 pointer-coarse:p-0' : ''}`}
156
+ onClick={() => {
157
+ token.remove?.onRemove();
158
+ input.current?.focus();
159
+ }}
160
+ >
161
+ <XIcon />
162
+ </button>
163
+ ) : null}
164
+ </li>
165
+ ))}
166
+ </ul>
167
+ ) : null}
168
+ <input
169
+ ref={input}
170
+ role="combobox"
171
+ aria-label={label}
172
+ aria-expanded
173
+ aria-controls={listId}
174
+ aria-autocomplete="list"
175
+ aria-activedescendant={activeValue ? optionId(listId, activeValue) : undefined}
176
+ aria-busy={busy}
177
+ placeholder={placeholder}
178
+ value={query}
179
+ className={`text-base-content placeholder:text-base-600 min-w-0 flex-1 bg-transparent text-base outline-none sm:text-sm ${rows ? 'h-8 min-w-32' : ''}`}
180
+ onChange={(event) => onQueryChange(event.target.value)}
181
+ onKeyDown={(event) => {
182
+ if (event.nativeEvent.isComposing) return;
183
+
184
+ if (event.key === 'Backspace' && !query && leaveLast) {
185
+ event.preventDefault();
186
+ leaveLast();
187
+ } else if ((event.key === 'ArrowDown' || event.key === 'ArrowUp') && options.length) {
188
+ event.preventDefault();
189
+
190
+ const index = activeValue ? options.indexOf(activeValue) : -1;
191
+ const step = event.key === 'ArrowDown' ? 1 : -1;
192
+
193
+ setActiveValue(options[(index + step + options.length) % options.length]!);
194
+ } else if (event.key === 'Enter' && activeValue) {
195
+ event.preventDefault();
196
+ select(activeValue);
197
+ }
198
+ }}
199
+ />
200
+ </div>
201
+ {trailing ? (
202
+ // Under a coarse pointer the control is 44 px, as a token's remove button is.
203
+ <div
204
+ className="flex min-h-8 shrink-0 items-center pointer-coarse:[&_[data-slot=button]]:h-11!"
205
+ onClick={() => input.current?.focus()}
206
+ >
207
+ {trailing}
208
+ </div>
100
209
  ) : null}
101
- <input
102
- ref={input}
103
- role="combobox"
104
- aria-label={label}
105
- aria-expanded
106
- aria-controls={listId}
107
- aria-autocomplete="list"
108
- aria-activedescendant={activeValue ? optionId(listId, activeValue) : undefined}
109
- aria-busy={busy}
110
- placeholder={placeholder}
111
- value={query}
112
- className="text-base-content placeholder:text-base-600 min-w-0 flex-1 bg-transparent text-base outline-none sm:text-sm"
113
- onChange={(event) => onQueryChange(event.target.value)}
114
- onKeyDown={(event) => {
115
- if (event.nativeEvent.isComposing) return;
116
-
117
- if (event.key === 'Backspace' && !query && scope) {
118
- event.preventDefault();
119
- scope.onLeave();
120
- } else if ((event.key === 'ArrowDown' || event.key === 'ArrowUp') && options.length) {
121
- event.preventDefault();
122
-
123
- const index = activeValue ? options.indexOf(activeValue) : -1;
124
- const step = event.key === 'ArrowDown' ? 1 : -1;
125
-
126
- setActiveValue(options[(index + step + options.length) % options.length]!);
127
- } else if (event.key === 'Enter' && activeValue) {
128
- event.preventDefault();
129
- onSelect(activeValue);
130
- }
131
- }}
132
- />
133
210
  <span className="flex sm:hidden">
134
211
  <IconButton aria-label={labels.close} icon={<XIcon />} onClick={onClose} variant="header" />
135
212
  </span>
136
213
  </div>
137
214
  <div ref={setViewport} className={`min-h-0 flex-1 overflow-y-auto overscroll-contain p-2 ${className}`}>
138
215
  <div ref={setContent} className="flow-root">
139
- <OptionContext.Provider value={{ listId, activeValue, setActiveValue }}>
216
+ <OptionContext.Provider value={{ listId, activeValue, setActiveValue, select }}>
140
217
  <ResultList id={listId} label={listLabel} density="compact" aria-busy={busy}>
141
218
  {children}
142
219
  </ResultList>
143
220
  </OptionContext.Provider>
144
- {options.length === 0 && empty ? (
145
- <Text tone="muted" className="px-3 py-8 text-center">
146
- {empty}
147
- </Text>
148
- ) : null}
221
+ <div role="status">
222
+ {(busy && busyLabel) || (options.length === 0 && empty) ? (
223
+ <Text as="div" tone="muted" className="px-3 py-8 text-center">
224
+ {busy && busyLabel ? busyLabel : empty}
225
+ </Text>
226
+ ) : null}
227
+ </div>
149
228
  </div>
150
229
  </div>
151
230
  {footer ? (
@@ -178,6 +257,57 @@ export function CommandPaletteGroup({ label, children }: { label: string; childr
178
257
  );
179
258
  }
180
259
 
260
+ /** One line of the palette's list: an icon, the name, an optional hint (searched with the name) and meta (on wider
261
+ * screens), and a tick while `checked`, as a filter that is on. Choosing it keeps the focus in the field. */
262
+ export function CommandPaletteOption({
263
+ value,
264
+ icon,
265
+ name,
266
+ hint,
267
+ meta,
268
+ checked,
269
+ }: {
270
+ value: string;
271
+ icon: ReactNode;
272
+ name: string;
273
+ hint?: string;
274
+ meta?: string;
275
+ /** Whether a filter's option is on; other options leave it out. */
276
+ checked?: boolean;
277
+ }) {
278
+ const context = useContext(OptionContext);
279
+
280
+ if (!context) throw new Error('CommandPaletteOption must be used inside CommandPalette');
281
+
282
+ return (
283
+ <div
284
+ id={optionId(context.listId, value)}
285
+ role="option"
286
+ aria-selected={context.activeValue === value}
287
+ aria-checked={checked}
288
+ tabIndex={-1}
289
+ onPointerMove={() => {
290
+ if (context.activeValue !== value) context.setActiveValue(value);
291
+ }}
292
+ onMouseDown={(event) => event.preventDefault()}
293
+ onClick={() => context.select(value)}
294
+ className="text-base-800 aria-selected:bg-primary/10 aria-selected:ring-primary/30 dark:text-base-content flex h-8 cursor-pointer items-center gap-2 rounded-md px-2 text-sm aria-selected:ring-1 aria-selected:ring-inset pointer-coarse:h-11"
295
+ >
296
+ <span className="text-base-700 grid size-4 shrink-0 place-items-center [&>svg]:size-3.5">{icon}</span>
297
+ <span title={name} className="min-w-0 flex-1 truncate">
298
+ {name}
299
+ </span>
300
+ {hint ? (
301
+ <span className="text-base-700 max-w-40 shrink-0 truncate text-xs tabular-nums">{hint}</span>
302
+ ) : null}
303
+ {meta ? (
304
+ <span className="text-base-700 max-w-56 shrink-0 truncate text-xs max-sm:hidden">{meta}</span>
305
+ ) : null}
306
+ {checked ? <CheckIcon aria-hidden="true" className="text-primary size-3.5 shrink-0" /> : null}
307
+ </div>
308
+ );
309
+ }
310
+
181
311
  /** Props for a palette option, such as a `ResultRow`: its id, highlight and pointer tracking. */
182
312
  export function useCommandPaletteOption(value: string) {
183
313
  const context = useContext(OptionContext);
@@ -195,5 +325,5 @@ export function useCommandPaletteOption(value: string) {
195
325
  }
196
326
 
197
327
  function optionId(listId: string, value: string) {
198
- return `${listId}-${value.replace(/[^\w-]/g, '_')}`;
328
+ return `${listId}-${encodeURIComponent(value)}`;
199
329
  }
@@ -8,7 +8,7 @@ import {
8
8
  type DropdownMenuRadioGroupProps,
9
9
  type DropdownMenuRadioItemProps,
10
10
  } from '@ninna-ui/overlays';
11
- import { type ComponentProps, forwardRef } from 'react';
11
+ import { type ComponentProps, forwardRef, type ReactNode } from 'react';
12
12
 
13
13
  interface DropdownMenuContentPolicyProps extends Omit<DropdownMenuContentProps, 'className'> {
14
14
  width?: 'sm' | 'md';
@@ -109,6 +109,16 @@ const DropdownMenuRadioItem = forwardRef<HTMLDivElement, DropdownMenuRadioItemPo
109
109
 
110
110
  DropdownMenuRadioItem.displayName = 'DropdownMenu.RadioItem';
111
111
 
112
+ /** An entry's label, truncating to the menu's width, with an optional smaller `detail` line under it. */
113
+ function DropdownMenuItemText({ children, detail }: { children: ReactNode; detail?: ReactNode }) {
114
+ return (
115
+ <span className="min-w-0 flex-1 truncate">
116
+ {children}
117
+ {detail && <span className="block truncate text-xs">{detail}</span>}
118
+ </span>
119
+ );
120
+ }
121
+
112
122
  function DropdownMenuRoot(props: ComponentProps<typeof NinnaDropdownMenu>) {
113
123
  return <NinnaDropdownMenu {...props} />;
114
124
  }
@@ -119,4 +129,5 @@ export const DropdownMenu = Object.assign(DropdownMenuRoot, NinnaDropdownMenu, {
119
129
  Label: DropdownMenuLabel,
120
130
  RadioGroup: DropdownMenuRadioGroup,
121
131
  RadioItem: DropdownMenuRadioItem,
132
+ ItemText: DropdownMenuItemText,
122
133
  });
@@ -0,0 +1,29 @@
1
+ import { CircleHelpIcon } from 'lucide-react';
2
+ import type { ReactNode } from 'react';
3
+
4
+ import { tabbable } from '../foundations/KeyboardFocus';
5
+ import { Popover } from './Popover';
6
+
7
+ /** A small help button after a setting's name that opens what the setting does in a popover. `label` is what it
8
+ * explains; the button reads "About {label}". */
9
+ export function HelpTip({ label, children }: { label: string; children: ReactNode }) {
10
+ return (
11
+ <Popover>
12
+ <Popover.Trigger asChild>
13
+ <button
14
+ {...tabbable}
15
+ type="button"
16
+ aria-label={`About ${label}`}
17
+ className="text-base-700 hover:text-base-content inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-full"
18
+ >
19
+ <CircleHelpIcon className="size-3.5" />
20
+ </button>
21
+ </Popover.Trigger>
22
+ <Popover.Content width="sm">
23
+ <div className="max-w-full text-xs leading-5 wrap-anywhere" data-help-text>
24
+ {children}
25
+ </div>
26
+ </Popover.Content>
27
+ </Popover>
28
+ );
29
+ }
@@ -33,9 +33,27 @@ interface ModalContentPolicyProps extends Omit<ModalContentProps, 'className' |
33
33
  sidebar?: ReactNode;
34
34
  /** The dialog's size; a dialog with a sidebar is `paned` unless it says otherwise. */
35
35
  presentation?: 'default' | 'paned' | 'workspace';
36
+ /** Grows the dialog to the window, less a small margin, under a 52px title bar; see ModalExpandButton. */
37
+ expanded?: boolean;
38
+ /** A paned dialog that keeps its width but takes its content's height, such as a short pane; a long list in it
39
+ * scrolls inside. Expanding wins. */
40
+ fitsContent?: boolean;
36
41
  title: string;
37
42
  }
38
43
 
44
+ /** The expanded dialog's size, inline so it wins over every presentation's size classes. */
45
+ const expandedStyle = {
46
+ width: 'calc(100vw - 2rem)',
47
+ maxWidth: 'none',
48
+ height: 'calc(100dvh - (52px + 1rem))',
49
+ maxHeight: 'none',
50
+ top: '52px',
51
+ translate: '-50% 0',
52
+ } as const;
53
+
54
+ /** A dialog that takes its content's height, inline so it wins over the paned size classes. */
55
+ const fitsContentStyle = { height: 'auto' } as const;
56
+
39
57
  const ModalContent = forwardRef<HTMLDivElement, ModalContentPolicyProps>(
40
58
  (
41
59
  {
@@ -49,6 +67,8 @@ const ModalContent = forwardRef<HTMLDivElement, ModalContentPolicyProps>(
49
67
  onPointerDownOutside,
50
68
  sidebar,
51
69
  presentation = sidebar ? 'paned' : 'default',
70
+ expanded = false,
71
+ fitsContent = false,
52
72
  title,
53
73
  ...props
54
74
  },
@@ -62,6 +82,9 @@ const ModalContent = forwardRef<HTMLDivElement, ModalContentPolicyProps>(
62
82
  ref={ref}
63
83
  {...props}
64
84
  {...focusProps}
85
+ {...(expanded
86
+ ? { 'data-expanded': true, style: expandedStyle }
87
+ : fitsContent && { style: fitsContentStyle })}
65
88
  title={accessibleTitle ?? title}
66
89
  description={description}
67
90
  closeOnEscape={loading ? false : closeOnEscape}
@@ -0,0 +1,48 @@
1
+ import { Maximize2Icon, Minimize2Icon } from 'lucide-react';
2
+ import { useState } from 'react';
3
+
4
+ import { IconButton } from '../actions/IconButton';
5
+ import { useDesignSystemLabels } from '../foundations/DesignSystemProvider';
6
+ import { Tooltip, TooltipPopup, TooltipTrigger } from './Tooltip';
7
+
8
+ interface ModalExpandButtonProps {
9
+ expanded: boolean;
10
+ onToggle: () => void;
11
+ /** The shortcut that toggles it too, as shown (⌘⇧E) and as `aria-keyshortcuts` reads it (Meta+Shift+E). */
12
+ shortcut?: { label: string; keys: string };
13
+ }
14
+
15
+ /**
16
+ * The control a dialog's header shows (in Modal.Content's `headerActions`) to grow the dialog to the window and back,
17
+ * with its shortcut in a tooltip. The tooltip opens on hover only: a dialog that opens with no field focuses this
18
+ * button, and a tooltip opened by that focus would take the dialog's first Escape. `aria-keyshortcuts` still tells
19
+ * assistive tech the shortcut.
20
+ */
21
+ export function ModalExpandButton({ expanded, onToggle, shortcut }: ModalExpandButtonProps) {
22
+ const labels = useDesignSystemLabels().dialog;
23
+ const label = expanded ? labels.restoreSize : labels.expand;
24
+ const [hint, setHint] = useState(false);
25
+
26
+ return (
27
+ <Tooltip
28
+ open={hint}
29
+ onOpenChange={(open) => setHint(open && document.querySelector('button[data-expand]:hover') !== null)}
30
+ >
31
+ <TooltipTrigger asChild>
32
+ <IconButton
33
+ data-expand
34
+ variant="header"
35
+ aria-label={label}
36
+ aria-pressed={expanded}
37
+ aria-keyshortcuts={shortcut?.keys}
38
+ icon={expanded ? <Minimize2Icon /> : <Maximize2Icon />}
39
+ onClick={onToggle}
40
+ />
41
+ </TooltipTrigger>
42
+ <TooltipPopup side="bottom">
43
+ {label}
44
+ {shortcut ? <span className="text-base-500 ms-2">{shortcut.label}</span> : null}
45
+ </TooltipPopup>
46
+ </Tooltip>
47
+ );
48
+ }
@@ -8,12 +8,14 @@ interface PopoverContentPolicyProps extends Omit<PopoverContentProps, 'className
8
8
  onOpenAutoFocus?: (event: Event) => void;
9
9
  onCloseAutoFocus?: (event: Event) => void;
10
10
  presentation?: 'default' | 'menu';
11
- width?: 'content' | 'sm' | 'trigger';
11
+ /** `sm` is 16rem and `md` 22rem, such as a chooser with a list; either stays inside the window. */
12
+ width?: 'content' | 'sm' | 'md' | 'trigger';
12
13
  }
13
14
 
14
15
  const popoverWidthClasses: Record<NonNullable<PopoverContentPolicyProps['width']>, string | undefined> = {
15
16
  content: undefined,
16
17
  sm: 'w-64',
18
+ md: 'w-88',
17
19
  trigger: 'w-(--radix-popover-trigger-width)',
18
20
  };
19
21
 
@@ -1,8 +1,16 @@
1
1
  export { ActionModal } from './ActionModal';
2
- export { CommandPalette, CommandPaletteGroup, useCommandPaletteOption } from './CommandPalette';
2
+ export {
3
+ CommandPalette,
4
+ CommandPaletteGroup,
5
+ CommandPaletteOption,
6
+ type CommandPaletteToken,
7
+ useCommandPaletteOption,
8
+ } from './CommandPalette';
3
9
  export { Drawer } from './Drawer';
4
10
  export { DropdownMenu } from './DropdownMenu';
11
+ export { HelpTip } from './HelpTip';
5
12
  export { ImageViewer } from './ImageViewer';
6
13
  export { Modal } from './Modal';
14
+ export { ModalExpandButton } from './ModalExpandButton';
7
15
  export { Popover } from './Popover';
8
16
  export { Tooltip, TooltipPopup, TooltipProvider, TooltipTrigger } from './Tooltip';
package/src/styles.css CHANGED
@@ -1,10 +1,12 @@
1
1
  /* Everything an app needs from the design system in one import: Tailwind, ninna-ui's preset, Lar's tokens and
2
2
  base styles, and the components' utilities, which Tailwind finds by scanning this package's sources. The
3
- marketing showcase is left out; a site that uses it imports showcase.css instead. */
3
+ marketing showcase and Lar Factory's board are left out; a site that uses the showcase imports showcase.css, and
4
+ the board imports board.css, instead. */
4
5
  @import './foundations/theme.css';
5
6
 
6
7
  @source './';
7
8
  @source not './showcase';
9
+ @source not './board';
8
10
 
9
11
  /* ninna-ui's own @source paths assume npm's flat node_modules and find nothing under pnpm, so its components'
10
12
  classes are scanned from here: next to this package once installed (pnpm's store or npm's tree), and in this
@@ -0,0 +1,24 @@
1
+ import { Code as NinnaCode, type CodeProps as NinnaCodeProps } from '@ninna-ui/primitives';
2
+ import { forwardRef } from 'react';
3
+
4
+ const wrapping = { anywhere: 'break-all', never: 'whitespace-nowrap' } as const;
5
+
6
+ export type CodeProps = NinnaCodeProps & {
7
+ /** A path or command inside a sentence, in the page's plain inline code with no chip: a path may break `anywhere`,
8
+ * a command `never`. Without it, the code chip. */
9
+ wrap?: keyof typeof wrapping;
10
+ };
11
+
12
+ /** Inline code: ninna-ui's code chip, or with `wrap`, plain inline code that breaks anywhere or never. */
13
+ export const Code = forwardRef<HTMLElement, CodeProps>(({ wrap, ...props }, ref) => {
14
+ if (wrap)
15
+ return (
16
+ <code ref={ref} className={wrapping[wrap]}>
17
+ {props.children}
18
+ </code>
19
+ );
20
+
21
+ return <NinnaCode ref={ref} {...props} />;
22
+ });
23
+
24
+ Code.displayName = 'Code';
@@ -89,17 +89,26 @@ function navigationClick({
89
89
  };
90
90
  }
91
91
 
92
- /** A link inside running text or a note: the surrounding size and colour, underlined. */
93
- export function InlineLink({ href, children }: { href: string; children?: ReactNode }) {
92
+ const inline =
93
+ 'focus-visible:outline-primary text-inherit underline decoration-current/40 underline-offset-2 transition-colors hover:decoration-current focus-visible:outline-2';
94
+
95
+ /** A link inside running text or a note: the surrounding size and colour, underlined. With `onClick` in place of
96
+ * `href` it is a button with the same look, such as "Change" after what was kept, and can be `disabled`. */
97
+ export function InlineLink(
98
+ props:
99
+ | { href: string; children?: ReactNode }
100
+ | { onClick: () => void; disabled?: boolean | undefined; children?: ReactNode },
101
+ ) {
102
+ if ('onClick' in props)
103
+ return (
104
+ <button {...tabbable} type="button" disabled={props.disabled} onClick={props.onClick} className={inline}>
105
+ {props.children}
106
+ </button>
107
+ );
108
+
94
109
  return (
95
- <a
96
- {...tabbable}
97
- href={href}
98
- target="_blank"
99
- rel="noreferrer"
100
- className="focus-visible:outline-primary text-inherit underline decoration-current/40 underline-offset-2 transition-colors hover:decoration-current focus-visible:outline-2"
101
- >
102
- {children}
110
+ <a {...tabbable} href={props.href} target="_blank" rel="noreferrer" className={inline}>
111
+ {props.children}
103
112
  </a>
104
113
  );
105
114
  }