@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
@@ -0,0 +1,175 @@
1
+ import { MarkdownToolbar } from "./MarkdownToolbar.js";
2
+ import { useCallback, useEffect } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { CodeNode } from "@lexical/code";
5
+ import { $generateNodesFromDOM } from "@lexical/html";
6
+ import { LinkNode } from "@lexical/link";
7
+ import { $isListItemNode, ListItemNode, ListNode } from "@lexical/list";
8
+ import { $convertFromMarkdownString, $convertToMarkdownString, $generateNodesFromMarkdownString, TRANSFORMERS } from "@lexical/markdown";
9
+ import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
10
+ import { LexicalComposer } from "@lexical/react/LexicalComposer";
11
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
12
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
13
+ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
14
+ import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
15
+ import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
16
+ import { ListPlugin } from "@lexical/react/LexicalListPlugin";
17
+ import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
18
+ import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
19
+ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
20
+ import { HeadingNode, QuoteNode } from "@lexical/rich-text";
21
+ import { $findMatchingParent, $getRoot, $getSelection, $insertNodes, $isParagraphNode, $isRangeSelection, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, INDENT_CONTENT_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND } from "lexical";
22
+ // src/forms/MarkdownEditor.tsx
23
+ const theme = {
24
+ heading: {
25
+ h1: "text-xl font-semibold mt-4 mb-1.5 first:mt-0",
26
+ h2: "text-lg font-semibold mt-4 mb-1 first:mt-0",
27
+ h3: "font-semibold mt-3 mb-1 first:mt-0"
28
+ },
29
+ list: {
30
+ ul: "list-disc ps-6 my-1.5",
31
+ ol: "list-decimal ps-6 my-1.5",
32
+ listitem: "my-0.5 marker:text-base-500",
33
+ nested: { listitem: "list-none" }
34
+ },
35
+ paragraph: "my-1.5 first:mt-0",
36
+ quote: "border-s-2 border-base-300 ps-3 text-base-700 my-1.5",
37
+ code: "block rounded-md bg-base-200 p-3 font-mono text-xs my-1.5 whitespace-pre-wrap",
38
+ link: "text-(--lar-info-text) underline underline-offset-2",
39
+ text: {
40
+ bold: "font-semibold",
41
+ italic: "italic",
42
+ strikethrough: "line-through",
43
+ code: "rounded bg-base-200 px-1 font-mono text-[0.85em]"
44
+ }
45
+ };
46
+ const LOOKS_LIKE_MARKDOWN = /(^|\n)\s*([-*+] |\d+[.)] |#{1,6} |> |```)|\*\*[^*]+\*\*|\[[^\]]+\]\([^)]+\)/;
47
+ /** Puts pasted `nodes` in place of the selected text. Ones starting with a heading, list, quote or code block go in after
48
+ * the block the caret is in, unless it is empty, as inserted at the caret the first block's text would join that block's. */
49
+ function $insertPasted(nodes) {
50
+ const selection = $getSelection();
51
+ if ($isRangeSelection(selection)) selection.removeText();
52
+ const block = $isRangeSelection(selection) ? selection.anchor.getNode().getTopLevelElement() : null;
53
+ const first = nodes[0];
54
+ if (!block || !first || $isParagraphNode(first) || block.getTextContentSize() === 0) return $insertNodes(nodes);
55
+ let last = block;
56
+ for (const node of nodes) last = last.insertAfter(node);
57
+ last.selectEnd();
58
+ }
59
+ /** Plain text that reads as Markdown, pasted without HTML, goes in as the nodes it stands for. */
60
+ function MarkdownPastePlugin() {
61
+ const [editor] = useLexicalComposerContext();
62
+ useEffect(() => editor.registerCommand(PASTE_COMMAND, (event) => {
63
+ const data = "clipboardData" in event ? event.clipboardData : null;
64
+ if (!data || data.types.includes("text/html")) return false;
65
+ const text = data.getData("text/plain");
66
+ if (!LOOKS_LIKE_MARKDOWN.test(text)) return false;
67
+ event.preventDefault();
68
+ editor.update(() => $insertPasted($generateNodesFromMarkdownString(text, TRANSFORMERS)));
69
+ return true;
70
+ }, COMMAND_PRIORITY_HIGH), [editor]);
71
+ return null;
72
+ }
73
+ /** Tab nests the list item the selection is in, one deeper than the item before it at most, and Shift+Tab lifts it back
74
+ * out. Where neither can change anything, as outside a list or on a top-level item, Tab is left to the browser, so it
75
+ * moves focus on rather than trapping it in the editor. */
76
+ function ListTabPlugin() {
77
+ const [editor] = useLexicalComposerContext();
78
+ useEffect(() => editor.registerCommand(KEY_TAB_COMMAND, (event) => {
79
+ const selection = $getSelection();
80
+ if (!$isRangeSelection(selection)) return false;
81
+ const items = selection.getNodes().map((node) => $isListItemNode(node) ? node : $findMatchingParent(node, $isListItemNode));
82
+ const moves = (item) => item !== null && (event.shiftKey ? item.getIndent() > 0 : item.getPreviousSibling() !== null);
83
+ if (!items.some(moves)) return false;
84
+ event.preventDefault();
85
+ return editor.dispatchCommand(event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND, void 0);
86
+ }, COMMAND_PRIORITY_EDITOR), [editor]);
87
+ return null;
88
+ }
89
+ /** Locks the text, as a disabled field does. */
90
+ function EditablePlugin({ editable }) {
91
+ const [editor] = useLexicalComposerContext();
92
+ useEffect(() => editor.setEditable(editable), [editor, editable]);
93
+ return null;
94
+ }
95
+ /** Reports the Markdown the editor makes of its starting text, which a check for changes compares against. */
96
+ function BaselinePlugin({ onBaseline }) {
97
+ const [editor] = useLexicalComposerContext();
98
+ useEffect(() => editor.getEditorState().read(() => onBaseline($convertToMarkdownString(TRANSFORMERS))), [editor, onBaseline]);
99
+ return null;
100
+ }
101
+ /** Fills a new editor from `value`, read as Markdown or as HTML. */
102
+ const startFrom = (value, format) => (editor) => {
103
+ if (format === "markdown") return $convertFromMarkdownString(value, TRANSFORMERS);
104
+ $getRoot().select();
105
+ $insertNodes($generateNodesFromDOM(editor, new DOMParser().parseFromString(value, "text/html")));
106
+ };
107
+ /** A rich text field that reads and reports Markdown, such as a ticket's description, with a formatting toolbar. */
108
+ function MarkdownEditor({ value, format = "markdown", onChange, label, placeholder, fill = false, disabled = false, autoFocus = false, onBaseline }) {
109
+ const narrow = fill === "wide" ? {
110
+ box: "@max-[40rem]:flex-none @max-[40rem]:overflow-visible",
111
+ area: "@max-[40rem]:flex-none @max-[40rem]:overflow-visible",
112
+ text: "@max-[40rem]:min-h-44",
113
+ toolbar: "@max-[40rem]:sticky @max-[40rem]:top-0 @max-[40rem]:z-10 @max-[40rem]:rounded-t-lg @max-[40rem]:bg-base-100"
114
+ } : {
115
+ box: "",
116
+ area: "",
117
+ text: "",
118
+ toolbar: ""
119
+ };
120
+ const onEdit = useCallback((state) => state.read(() => onChange($convertToMarkdownString(TRANSFORMERS))), [onChange]);
121
+ return /* @__PURE__ */ jsxs(LexicalComposer, {
122
+ initialConfig: {
123
+ namespace: "description",
124
+ theme,
125
+ onError: (error) => {
126
+ throw error;
127
+ },
128
+ nodes: [
129
+ HeadingNode,
130
+ QuoteNode,
131
+ ListNode,
132
+ ListItemNode,
133
+ CodeNode,
134
+ LinkNode
135
+ ],
136
+ editorState: startFrom(value, format)
137
+ },
138
+ children: [
139
+ /* @__PURE__ */ jsxs("div", {
140
+ "data-description-editor": true,
141
+ className: `border-base-300 bg-base-100 focus-within:border-primary/60 focus-within:ring-primary/15 flex flex-col overflow-hidden rounded-lg border transition-[border-color,box-shadow] focus-within:ring-3 ${fill ? `min-h-0 flex-1 ${narrow.box}` : ""}`,
142
+ children: [/* @__PURE__ */ jsx(MarkdownToolbar, { className: narrow.toolbar }), /* @__PURE__ */ jsx("div", {
143
+ className: `relative overflow-y-auto ${fill ? `min-h-0 flex-1 ${narrow.area}` : "max-h-[min(24rem,45dvh)]"}`,
144
+ children: /* @__PURE__ */ jsx(RichTextPlugin, {
145
+ contentEditable: /* @__PURE__ */ jsx(ContentEditable, {
146
+ "aria-label": label,
147
+ className: `px-4 py-3 text-[15px] leading-relaxed outline-none ${fill ? `min-h-full ${narrow.text}` : "min-h-44"}`
148
+ }),
149
+ placeholder: /* @__PURE__ */ jsx("div", {
150
+ className: "text-base-700 pointer-events-none absolute top-3 left-4 text-[15px] leading-relaxed",
151
+ children: placeholder
152
+ }),
153
+ ErrorBoundary: LexicalErrorBoundary
154
+ })
155
+ })]
156
+ }),
157
+ autoFocus && /* @__PURE__ */ jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }),
158
+ /* @__PURE__ */ jsx(HistoryPlugin, {}),
159
+ /* @__PURE__ */ jsx(ListPlugin, {}),
160
+ /* @__PURE__ */ jsx(LinkPlugin, {}),
161
+ /* @__PURE__ */ jsx(ListTabPlugin, {}),
162
+ /* @__PURE__ */ jsx(MarkdownShortcutPlugin, { transformers: TRANSFORMERS }),
163
+ /* @__PURE__ */ jsx(MarkdownPastePlugin, {}),
164
+ /* @__PURE__ */ jsx(EditablePlugin, { editable: !disabled }),
165
+ onBaseline && /* @__PURE__ */ jsx(BaselinePlugin, { onBaseline }),
166
+ /* @__PURE__ */ jsx(OnChangePlugin, {
167
+ ignoreSelectionChange: true,
168
+ onChange: onEdit
169
+ })
170
+ ]
171
+ });
172
+ }
173
+ export { MarkdownEditor };
174
+
175
+ //# sourceMappingURL=MarkdownEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../src/forms/MarkdownEditor.tsx"],"sourcesContent":["// The Markdown editor: Lexical, formatting as you type and storing Markdown, under a light toolbar. \"- \" starts a\n// bullet list, \"1. \" a numbered list, \"# \" a heading and **bold** turns bold; pasted plain-text Markdown renders. It\n// opens HTML, as an older Azure DevOps description holds, as the nodes it stands for, and still reports Markdown.\n\nimport { CodeNode } from '@lexical/code';\nimport { $generateNodesFromDOM } from '@lexical/html';\nimport { LinkNode } from '@lexical/link';\nimport { $isListItemNode, ListItemNode, ListNode } from '@lexical/list';\nimport {\n $convertFromMarkdownString,\n $convertToMarkdownString,\n $generateNodesFromMarkdownString,\n TRANSFORMERS,\n} from '@lexical/markdown';\nimport { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';\nimport { LexicalComposer } from '@lexical/react/LexicalComposer';\nimport { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';\nimport { ContentEditable } from '@lexical/react/LexicalContentEditable';\nimport { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';\nimport { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';\nimport { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';\nimport { ListPlugin } from '@lexical/react/LexicalListPlugin';\nimport { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin';\nimport { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';\nimport { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';\nimport { HeadingNode, QuoteNode } from '@lexical/rich-text';\nimport {\n $findMatchingParent,\n $getRoot,\n $getSelection,\n $insertNodes,\n $isParagraphNode,\n $isRangeSelection,\n COMMAND_PRIORITY_EDITOR,\n COMMAND_PRIORITY_HIGH,\n INDENT_CONTENT_COMMAND,\n KEY_TAB_COMMAND,\n OUTDENT_CONTENT_COMMAND,\n PASTE_COMMAND,\n} from 'lexical';\nimport type { EditorState, LexicalEditor, LexicalNode } from 'lexical';\nimport { useCallback, useEffect } from 'react';\n\nimport type { RichTextFormat } from '../display/RichText';\nimport { MarkdownToolbar } from './MarkdownToolbar';\n\nconst theme = {\n heading: {\n h1: 'text-xl font-semibold mt-4 mb-1.5 first:mt-0',\n h2: 'text-lg font-semibold mt-4 mb-1 first:mt-0',\n h3: 'font-semibold mt-3 mb-1 first:mt-0',\n },\n list: {\n ul: 'list-disc ps-6 my-1.5',\n ol: 'list-decimal ps-6 my-1.5',\n listitem: 'my-0.5 marker:text-base-500',\n nested: { listitem: 'list-none' },\n },\n paragraph: 'my-1.5 first:mt-0',\n quote: 'border-s-2 border-base-300 ps-3 text-base-700 my-1.5',\n code: 'block rounded-md bg-base-200 p-3 font-mono text-xs my-1.5 whitespace-pre-wrap',\n link: 'text-(--lar-info-text) underline underline-offset-2',\n text: {\n bold: 'font-semibold',\n italic: 'italic',\n strikethrough: 'line-through',\n code: 'rounded bg-base-200 px-1 font-mono text-[0.85em]',\n },\n};\n\nconst LOOKS_LIKE_MARKDOWN = /(^|\\n)\\s*([-*+] |\\d+[.)] |#{1,6} |> |```)|\\*\\*[^*]+\\*\\*|\\[[^\\]]+\\]\\([^)]+\\)/;\n\n/** Puts pasted `nodes` in place of the selected text. Ones starting with a heading, list, quote or code block go in after\n * the block the caret is in, unless it is empty, as inserted at the caret the first block's text would join that block's. */\nfunction $insertPasted(nodes: LexicalNode[]) {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection)) selection.removeText();\n\n const block = $isRangeSelection(selection) ? selection.anchor.getNode().getTopLevelElement() : null;\n const first = nodes[0];\n\n if (!block || !first || $isParagraphNode(first) || block.getTextContentSize() === 0) return $insertNodes(nodes);\n\n let last: LexicalNode = block;\n\n for (const node of nodes) last = last.insertAfter(node);\n last.selectEnd();\n}\n\n/** Plain text that reads as Markdown, pasted without HTML, goes in as the nodes it stands for. */\nfunction MarkdownPastePlugin() {\n const [editor] = useLexicalComposerContext();\n\n useEffect(\n () =>\n editor.registerCommand(\n PASTE_COMMAND,\n (event) => {\n const data = 'clipboardData' in event ? event.clipboardData : null;\n\n if (!data || data.types.includes('text/html')) return false;\n\n const text = data.getData('text/plain');\n\n if (!LOOKS_LIKE_MARKDOWN.test(text)) return false;\n\n event.preventDefault();\n editor.update(() => $insertPasted($generateNodesFromMarkdownString(text, TRANSFORMERS)));\n\n return true;\n },\n COMMAND_PRIORITY_HIGH,\n ),\n [editor],\n );\n\n return null;\n}\n\n/** Tab nests the list item the selection is in, one deeper than the item before it at most, and Shift+Tab lifts it back\n * out. Where neither can change anything, as outside a list or on a top-level item, Tab is left to the browser, so it\n * moves focus on rather than trapping it in the editor. */\nfunction ListTabPlugin() {\n const [editor] = useLexicalComposerContext();\n\n useEffect(\n () =>\n editor.registerCommand(\n KEY_TAB_COMMAND,\n (event) => {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) return false;\n\n const items = selection\n .getNodes()\n .map((node) => ($isListItemNode(node) ? node : $findMatchingParent(node, $isListItemNode)));\n const moves = (item: ListItemNode | null) =>\n item !== null && (event.shiftKey ? item.getIndent() > 0 : item.getPreviousSibling() !== null);\n\n if (!items.some(moves)) return false;\n\n event.preventDefault();\n\n return editor.dispatchCommand(\n event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND,\n undefined,\n );\n },\n COMMAND_PRIORITY_EDITOR,\n ),\n [editor],\n );\n\n return null;\n}\n\n/** Locks the text, as a disabled field does. */\nfunction EditablePlugin({ editable }: { editable: boolean }) {\n const [editor] = useLexicalComposerContext();\n\n useEffect(() => editor.setEditable(editable), [editor, editable]);\n\n return null;\n}\n\n/** Reports the Markdown the editor makes of its starting text, which a check for changes compares against. */\nfunction BaselinePlugin({ onBaseline }: { onBaseline: (markdown: string) => void }) {\n const [editor] = useLexicalComposerContext();\n\n useEffect(\n () => editor.getEditorState().read(() => onBaseline($convertToMarkdownString(TRANSFORMERS))),\n [editor, onBaseline],\n );\n\n return null;\n}\n\n/** Fills a new editor from `value`, read as Markdown or as HTML. */\nconst startFrom = (value: string, format: RichTextFormat) => (editor: LexicalEditor) => {\n if (format === 'markdown') return $convertFromMarkdownString(value, TRANSFORMERS);\n\n $getRoot().select();\n $insertNodes($generateNodesFromDOM(editor, new DOMParser().parseFromString(value, 'text/html')));\n};\n\ninterface MarkdownEditorProps {\n /** The Markdown it starts from; later changes to it are ignored. */\n value: string;\n /** How `value` is written; the editor reports Markdown either way. */\n format?: RichTextFormat;\n onChange: (markdown: string) => void;\n label: string;\n placeholder: string;\n /** Grows the editor into its parent's height, or, when 'wide', only while its container is 40rem wide or more and\n * otherwise with its text, uncapped, as its parent scrolls. Without it, it grows with its text up to a cap. */\n fill?: boolean | 'wide';\n disabled?: boolean;\n autoFocus?: boolean;\n /** Called with the Markdown the editor makes of `value`, before any edit. */\n onBaseline?: (markdown: string) => void;\n}\n\n/** A rich text field that reads and reports Markdown, such as a ticket's description, with a formatting toolbar. */\nexport function MarkdownEditor({\n value,\n format = 'markdown',\n onChange,\n label,\n placeholder,\n fill = false,\n disabled = false,\n autoFocus = false,\n onBaseline,\n}: MarkdownEditorProps) {\n // Below 40rem a 'wide' fill gives way to the editor's text height, with no scroll area of its own. The toolbar then\n // sticks to the top of the parent's scroll area, which needs the box to stop clipping, so the toolbar rounds its own\n // corners instead.\n const narrow =\n fill === 'wide'\n ? {\n box: '@max-[40rem]:flex-none @max-[40rem]:overflow-visible',\n area: '@max-[40rem]:flex-none @max-[40rem]:overflow-visible',\n text: '@max-[40rem]:min-h-44',\n toolbar:\n '@max-[40rem]:sticky @max-[40rem]:top-0 @max-[40rem]:z-10 @max-[40rem]:rounded-t-lg @max-[40rem]:bg-base-100',\n }\n : { box: '', area: '', text: '', toolbar: '' };\n const onEdit = useCallback(\n (state: EditorState) => state.read(() => onChange($convertToMarkdownString(TRANSFORMERS))),\n [onChange],\n );\n\n return (\n <LexicalComposer\n initialConfig={{\n namespace: 'description',\n theme,\n onError: (error) => {\n throw error;\n },\n nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, CodeNode, LinkNode],\n editorState: startFrom(value, format),\n }}\n >\n <div\n data-description-editor\n className={`border-base-300 bg-base-100 focus-within:border-primary/60 focus-within:ring-primary/15 flex flex-col overflow-hidden rounded-lg border transition-[border-color,box-shadow] focus-within:ring-3 ${fill ? `min-h-0 flex-1 ${narrow.box}` : ''}`}\n >\n <MarkdownToolbar className={narrow.toolbar} />\n <div\n className={`relative overflow-y-auto ${fill ? `min-h-0 flex-1 ${narrow.area}` : 'max-h-[min(24rem,45dvh)]'}`}\n >\n <RichTextPlugin\n contentEditable={\n <ContentEditable\n aria-label={label}\n className={`px-4 py-3 text-[15px] leading-relaxed outline-none ${fill ? `min-h-full ${narrow.text}` : 'min-h-44'}`}\n />\n }\n placeholder={\n <div className=\"text-base-700 pointer-events-none absolute top-3 left-4 text-[15px] leading-relaxed\">\n {placeholder}\n </div>\n }\n ErrorBoundary={LexicalErrorBoundary}\n />\n </div>\n </div>\n {autoFocus && <AutoFocusPlugin defaultSelection=\"rootEnd\" />}\n <HistoryPlugin />\n <ListPlugin />\n <LinkPlugin />\n <ListTabPlugin />\n <MarkdownShortcutPlugin transformers={TRANSFORMERS} />\n <MarkdownPastePlugin />\n <EditablePlugin editable={!disabled} />\n {onBaseline && <BaselinePlugin onBaseline={onBaseline} />}\n <OnChangePlugin ignoreSelectionChange onChange={onEdit} />\n </LexicalComposer>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,204 @@
1
+ import { Button } from "../actions/Button.js";
2
+ import { useDesignSystemLabels } from "../foundations/DesignSystemProvider.js";
3
+ import { applePlatform } from "../foundations/KeyboardShortcut.js";
4
+ import { Popover } from "../overlays/Popover.js";
5
+ import { Tooltip, TooltipPopup, TooltipTrigger } from "../overlays/Tooltip.js";
6
+ import { useEffect, useRef, useState } from "react";
7
+ import { BoldIcon, HeadingIcon, ItalicIcon, LinkIcon, ListIcon, ListOrderedIcon } from "lucide-react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { $isLinkNode, $toggleLink, TOGGLE_LINK_COMMAND } from "@lexical/link";
10
+ import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, REMOVE_LIST_COMMAND } from "@lexical/list";
11
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
12
+ import { $createHeadingNode, $isHeadingNode } from "@lexical/rich-text";
13
+ import { $createParagraphNode, $getSelection, $isRangeSelection, $setSelection, FORMAT_TEXT_COMMAND } from "lexical";
14
+ import { $setBlocksType } from "@lexical/selection";
15
+ // src/forms/MarkdownToolbar.tsx
16
+ const MOD = applePlatform ? "⌘" : "Ctrl+";
17
+ const NONE = {
18
+ bold: false,
19
+ italic: false,
20
+ link: false,
21
+ block: "paragraph"
22
+ };
23
+ /** What the selection has on. */
24
+ function readActive(state) {
25
+ return state.read(() => {
26
+ const selection = $getSelection();
27
+ if (!$isRangeSelection(selection)) return NONE;
28
+ const node = selection.anchor.getNode();
29
+ const top = node.getKey() === "root" ? null : node.getTopLevelElement();
30
+ const block = $isHeadingNode(top) ? "heading" : $isListNode(top) ? top.getListType() === "number" ? "number" : "bullet" : "paragraph";
31
+ return {
32
+ bold: selection.hasFormat("bold"),
33
+ italic: selection.hasFormat("italic"),
34
+ link: $isLinkNode(node.getParent()) || $isLinkNode(node),
35
+ block
36
+ };
37
+ });
38
+ }
39
+ /** A toolbar button; `quiet` keeps its tooltip shut, as while the field it opens would sit under it. Any other props,
40
+ * such as a popover trigger's, go on the button itself. */
41
+ function ToolButton({ label, keys, on, icon, run, quiet = false, ...button }) {
42
+ const [tip, setTip] = useState(false);
43
+ return /* @__PURE__ */ jsxs(Tooltip, {
44
+ open: tip && !quiet,
45
+ onOpenChange: (open) => setTip(open && !quiet),
46
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, {
47
+ asChild: true,
48
+ children: /* @__PURE__ */ jsx("button", {
49
+ type: "button",
50
+ ...button,
51
+ "aria-label": label,
52
+ "aria-pressed": on,
53
+ onMouseDown: (event) => event.preventDefault(),
54
+ onClick: (event) => {
55
+ run();
56
+ button.onClick?.(event);
57
+ },
58
+ className: `focus-visible:ring-primary grid size-7 place-items-center rounded-md transition-colors outline-none focus-visible:ring-2 [&_svg]:size-4 ${on ? "bg-primary/10 text-primary" : "text-base-700 hover:bg-base-200 hover:text-base-content"}`,
59
+ children: icon
60
+ })
61
+ }), /* @__PURE__ */ jsxs(TooltipPopup, {
62
+ side: "bottom",
63
+ children: [label, keys && /* @__PURE__ */ jsx("span", {
64
+ className: "text-base-500 ms-2",
65
+ children: keys
66
+ })]
67
+ })]
68
+ });
69
+ }
70
+ /** The URL field Link opens, anchored under its button; `close` gets the URL to link, or null for none. A popover is a
71
+ * layer above the dialog, so Escape closes the field alone. */
72
+ function LinkField({ open, close, children }) {
73
+ const labels = useDesignSystemLabels().editor;
74
+ const field = useRef(null);
75
+ const apply = () => close(field.current.value.trim() || null);
76
+ return /* @__PURE__ */ jsxs(Popover, {
77
+ open,
78
+ onOpenChange: (opened) => !opened && close(null),
79
+ children: [/* @__PURE__ */ jsx(Popover.Trigger, {
80
+ asChild: true,
81
+ onClick: (event) => event.preventDefault(),
82
+ children
83
+ }), /* @__PURE__ */ jsx(Popover.Content, {
84
+ side: "bottom",
85
+ align: "start",
86
+ onCloseAutoFocus: (event) => event.preventDefault(),
87
+ onInput: (event) => event.stopPropagation(),
88
+ onChange: (event) => event.stopPropagation(),
89
+ children: /* @__PURE__ */ jsxs("div", {
90
+ className: "flex items-center gap-1.5",
91
+ children: [/* @__PURE__ */ jsx("input", {
92
+ ref: field,
93
+ type: "url",
94
+ "aria-label": labels.linkUrl,
95
+ placeholder: labels.linkPlaceholder,
96
+ autoComplete: "off",
97
+ className: "border-base-300 bg-base-100 focus:border-primary/60 focus:ring-primary/15 h-7 w-56 max-w-full rounded-md border px-2 text-sm outline-none focus:ring-3",
98
+ onKeyDown: (event) => {
99
+ if (event.key !== "Enter") return;
100
+ event.preventDefault();
101
+ apply();
102
+ }
103
+ }), /* @__PURE__ */ jsx(Button, {
104
+ compact: true,
105
+ onClick: apply,
106
+ children: labels.apply
107
+ })]
108
+ })
109
+ })]
110
+ });
111
+ }
112
+ /** `className` adds to the bar's own classes. */
113
+ function MarkdownToolbar({ className = "" }) {
114
+ const labels = useDesignSystemLabels().editor;
115
+ const [editor] = useLexicalComposerContext();
116
+ const [active, setActive] = useState(NONE);
117
+ const [linking, setLinking] = useState(null);
118
+ useEffect(() => editor.registerUpdateListener(({ editorState }) => setActive(readActive(editorState))), [editor]);
119
+ const heading = () => editor.update(() => {
120
+ const selection = $getSelection();
121
+ if ($isRangeSelection(selection)) $setBlocksType(selection, () => active.block === "heading" ? $createParagraphNode() : $createHeadingNode("h2"));
122
+ });
123
+ const list = (type) => {
124
+ if (active.block === type) editor.dispatchCommand(REMOVE_LIST_COMMAND, void 0);
125
+ else editor.dispatchCommand(type === "bullet" ? INSERT_UNORDERED_LIST_COMMAND : INSERT_ORDERED_LIST_COMMAND, void 0);
126
+ };
127
+ const link = () => {
128
+ if (active.link) return editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
129
+ setLinking(editor.getEditorState().read(() => $getSelection()?.clone() ?? null));
130
+ };
131
+ const closeLink = (url) => {
132
+ const selection = linking;
133
+ setLinking(null);
134
+ if (url && selection) editor.update(() => {
135
+ $setSelection(selection.clone());
136
+ $toggleLink(url);
137
+ });
138
+ editor.focus();
139
+ };
140
+ return /* @__PURE__ */ jsxs("div", {
141
+ role: "toolbar",
142
+ "aria-label": labels.formatting,
143
+ className: `border-base-300/70 flex shrink-0 items-center gap-0.5 border-b px-2 py-1 ${className}`,
144
+ children: [
145
+ /* @__PURE__ */ jsx(ToolButton, {
146
+ label: labels.bold,
147
+ keys: `${MOD}B`,
148
+ on: active.bold,
149
+ icon: /* @__PURE__ */ jsx(BoldIcon, {}),
150
+ run: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold")
151
+ }),
152
+ /* @__PURE__ */ jsx(ToolButton, {
153
+ label: labels.italic,
154
+ keys: `${MOD}I`,
155
+ on: active.italic,
156
+ icon: /* @__PURE__ */ jsx(ItalicIcon, {}),
157
+ run: () => editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic")
158
+ }),
159
+ /* @__PURE__ */ jsx("span", {
160
+ "aria-hidden": "true",
161
+ className: "bg-base-300 mx-1 h-4 w-px"
162
+ }),
163
+ /* @__PURE__ */ jsx(ToolButton, {
164
+ label: labels.heading,
165
+ keys: "#",
166
+ on: active.block === "heading",
167
+ icon: /* @__PURE__ */ jsx(HeadingIcon, {}),
168
+ run: heading
169
+ }),
170
+ /* @__PURE__ */ jsx(ToolButton, {
171
+ label: labels.bulletedList,
172
+ keys: "-",
173
+ on: active.block === "bullet",
174
+ icon: /* @__PURE__ */ jsx(ListIcon, {}),
175
+ run: () => list("bullet")
176
+ }),
177
+ /* @__PURE__ */ jsx(ToolButton, {
178
+ label: labels.numberedList,
179
+ keys: "1.",
180
+ on: active.block === "number",
181
+ icon: /* @__PURE__ */ jsx(ListOrderedIcon, {}),
182
+ run: () => list("number")
183
+ }),
184
+ /* @__PURE__ */ jsx(LinkField, {
185
+ open: linking !== null,
186
+ close: closeLink,
187
+ children: /* @__PURE__ */ jsx(ToolButton, {
188
+ label: labels.link,
189
+ on: active.link,
190
+ icon: /* @__PURE__ */ jsx(LinkIcon, {}),
191
+ run: link,
192
+ quiet: linking !== null
193
+ })
194
+ }),
195
+ /* @__PURE__ */ jsx("span", {
196
+ className: "text-base-700 ms-auto pe-1 text-[11px]",
197
+ children: labels.markdown
198
+ })
199
+ ]
200
+ });
201
+ }
202
+ export { MarkdownToolbar };
203
+
204
+ //# sourceMappingURL=MarkdownToolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownToolbar.js","names":[],"sources":["../../src/forms/MarkdownToolbar.tsx"],"sourcesContent":["// The Markdown editor's toolbar: bold, italic, heading, bulleted list, numbered list and link, each pressed while\n// the selection has it. Link opens a URL field under the toolbar, as Electron has no window.prompt: Enter or Apply links\n// the selection, Escape, a click outside or an empty URL closes it with no link, and Escape leaves the dialog open. Its\n// tooltip stays shut while the field is open, so it covers none of it.\n\nimport { $isLinkNode, $toggleLink, TOGGLE_LINK_COMMAND } from '@lexical/link';\nimport {\n $isListNode,\n INSERT_ORDERED_LIST_COMMAND,\n INSERT_UNORDERED_LIST_COMMAND,\n REMOVE_LIST_COMMAND,\n} from '@lexical/list';\nimport { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';\nimport { $createHeadingNode, $isHeadingNode } from '@lexical/rich-text';\nimport { $setBlocksType } from '@lexical/selection';\nimport { $createParagraphNode, $getSelection, $isRangeSelection, $setSelection, FORMAT_TEXT_COMMAND } from 'lexical';\nimport type { BaseSelection, EditorState } from 'lexical';\nimport { BoldIcon, HeadingIcon, ItalicIcon, LinkIcon, ListIcon, ListOrderedIcon } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\nimport type { ComponentProps, ReactNode } from 'react';\n\nimport { Button } from '../actions/Button';\nimport { useDesignSystemLabels } from '../foundations/DesignSystemProvider';\nimport { applePlatform } from '../foundations/KeyboardShortcut';\nimport { Popover } from '../overlays/Popover';\nimport { Tooltip, TooltipPopup, TooltipTrigger } from '../overlays/Tooltip';\n\nconst MOD = applePlatform ? '⌘' : 'Ctrl+';\n\ntype Block = 'paragraph' | 'heading' | 'bullet' | 'number';\ninterface Active {\n bold: boolean;\n italic: boolean;\n link: boolean;\n block: Block;\n}\n\nconst NONE: Active = { bold: false, italic: false, link: false, block: 'paragraph' };\n\n/** What the selection has on. */\nfunction readActive(state: EditorState): Active {\n return state.read(() => {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) return NONE;\n\n const node = selection.anchor.getNode();\n const top = node.getKey() === 'root' ? null : node.getTopLevelElement();\n const block = $isHeadingNode(top)\n ? 'heading'\n : $isListNode(top)\n ? top.getListType() === 'number'\n ? 'number'\n : 'bullet'\n : 'paragraph';\n\n return {\n bold: selection.hasFormat('bold'),\n italic: selection.hasFormat('italic'),\n link: $isLinkNode(node.getParent()) || $isLinkNode(node),\n block,\n };\n });\n}\n\n/** A toolbar button; `quiet` keeps its tooltip shut, as while the field it opens would sit under it. Any other props,\n * such as a popover trigger's, go on the button itself. */\nfunction ToolButton({\n label,\n keys,\n on,\n icon,\n run,\n quiet = false,\n ...button\n}: {\n label: string;\n keys?: string;\n on: boolean;\n icon: ReactNode;\n run: () => void;\n quiet?: boolean;\n} & ComponentProps<'button'>) {\n // Held here, so a hover while quiet doesn't leave the tooltip to show once it no longer is.\n const [tip, setTip] = useState(false);\n\n return (\n <Tooltip open={tip && !quiet} onOpenChange={(open) => setTip(open && !quiet)}>\n <TooltipTrigger asChild>\n {/* Pressing keeps the editor's selection: focus stays in the editor. */}\n <button\n type=\"button\"\n {...button}\n aria-label={label}\n aria-pressed={on}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n run();\n button.onClick?.(event);\n }}\n className={`focus-visible:ring-primary grid size-7 place-items-center rounded-md transition-colors outline-none focus-visible:ring-2 [&_svg]:size-4 ${on ? 'bg-primary/10 text-primary' : 'text-base-700 hover:bg-base-200 hover:text-base-content'}`}\n >\n {icon}\n </button>\n </TooltipTrigger>\n <TooltipPopup side=\"bottom\">\n {label}\n {keys && <span className=\"text-base-500 ms-2\">{keys}</span>}\n </TooltipPopup>\n </Tooltip>\n );\n}\n\n/** The URL field Link opens, anchored under its button; `close` gets the URL to link, or null for none. A popover is a\n * layer above the dialog, so Escape closes the field alone. */\nfunction LinkField({\n open,\n close,\n children,\n}: {\n open: boolean;\n close: (url: string | null) => void;\n children: ReactNode;\n}) {\n const labels = useDesignSystemLabels().editor;\n const field = useRef<HTMLInputElement>(null);\n const apply = () => close(field.current!.value.trim() || null);\n\n return (\n <Popover open={open} onOpenChange={(opened) => !opened && close(null)}>\n {/* The button opens the field itself, so the trigger's own toggle is turned off. */}\n <Popover.Trigger asChild onClick={(event) => event.preventDefault()}>\n {children}\n </Popover.Trigger>\n {/* Portalled, its events still reach the form in the React tree, which would count them as edits. */}\n <Popover.Content\n side=\"bottom\"\n align=\"start\"\n onCloseAutoFocus={(event) => event.preventDefault()}\n onInput={(event) => event.stopPropagation()}\n onChange={(event) => event.stopPropagation()}\n >\n <div className=\"flex items-center gap-1.5\">\n <input\n ref={field}\n type=\"url\"\n aria-label={labels.linkUrl}\n placeholder={labels.linkPlaceholder}\n autoComplete=\"off\"\n className=\"border-base-300 bg-base-100 focus:border-primary/60 focus:ring-primary/15 h-7 w-56 max-w-full rounded-md border px-2 text-sm outline-none focus:ring-3\"\n onKeyDown={(event) => {\n if (event.key !== 'Enter') return;\n\n event.preventDefault();\n apply();\n }}\n />\n <Button compact onClick={apply}>\n {labels.apply}\n </Button>\n </div>\n </Popover.Content>\n </Popover>\n );\n}\n\n/** `className` adds to the bar's own classes. */\nexport function MarkdownToolbar({ className = '' }: { className?: string }) {\n const labels = useDesignSystemLabels().editor;\n const [editor] = useLexicalComposerContext();\n const [active, setActive] = useState(NONE);\n // The selection Link opened its field on, which the field's focus would otherwise lose.\n const [linking, setLinking] = useState<BaseSelection | null>(null);\n\n useEffect(() => editor.registerUpdateListener(({ editorState }) => setActive(readActive(editorState))), [editor]);\n\n const heading = () =>\n editor.update(() => {\n const selection = $getSelection();\n\n if ($isRangeSelection(selection))\n $setBlocksType(selection, () =>\n active.block === 'heading' ? $createParagraphNode() : $createHeadingNode('h2'),\n );\n });\n const list = (type: 'bullet' | 'number') => {\n if (active.block === type) editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);\n else\n editor.dispatchCommand(\n type === 'bullet' ? INSERT_UNORDERED_LIST_COMMAND : INSERT_ORDERED_LIST_COMMAND,\n undefined,\n );\n };\n const link = () => {\n if (active.link) return editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);\n\n setLinking(editor.getEditorState().read(() => $getSelection()?.clone() ?? null));\n };\n const closeLink = (url: string | null) => {\n const selection = linking;\n\n setLinking(null);\n\n if (url && selection)\n editor.update(() => {\n $setSelection(selection.clone());\n $toggleLink(url);\n });\n\n editor.focus();\n };\n\n return (\n <div\n role=\"toolbar\"\n aria-label={labels.formatting}\n className={`border-base-300/70 flex shrink-0 items-center gap-0.5 border-b px-2 py-1 ${className}`}\n >\n <ToolButton\n label={labels.bold}\n keys={`${MOD}B`}\n on={active.bold}\n icon={<BoldIcon />}\n run={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold')}\n />\n <ToolButton\n label={labels.italic}\n keys={`${MOD}I`}\n on={active.italic}\n icon={<ItalicIcon />}\n run={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic')}\n />\n <span aria-hidden=\"true\" className=\"bg-base-300 mx-1 h-4 w-px\" />\n <ToolButton\n label={labels.heading}\n keys=\"#\"\n on={active.block === 'heading'}\n icon={<HeadingIcon />}\n run={heading}\n />\n <ToolButton\n label={labels.bulletedList}\n keys=\"-\"\n on={active.block === 'bullet'}\n icon={<ListIcon />}\n run={() => list('bullet')}\n />\n <ToolButton\n label={labels.numberedList}\n keys=\"1.\"\n on={active.block === 'number'}\n icon={<ListOrderedIcon />}\n run={() => list('number')}\n />\n <LinkField open={linking !== null} close={closeLink}>\n <ToolButton\n label={labels.link}\n on={active.link}\n icon={<LinkIcon />}\n run={link}\n quiet={linking !== null}\n />\n </LinkField>\n <span className=\"text-base-700 ms-auto pe-1 text-[11px]\">{labels.markdown}</span>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,21 +1,42 @@
1
- interface LoadedOptionSelectProps<Value extends string> {
2
- disabled?: boolean;
1
+ import { SelectProps } from "./Select.js";
2
+ import { ReactNode, Ref } from "react";
3
+ export interface OptionSelectOption<Value extends string = string> {
3
4
  label: string;
5
+ value: Value;
6
+ /** Disabled options stay visible but cannot be chosen. */
7
+ disabled?: boolean;
8
+ icon?: ReactNode;
9
+ /** A lar-brand app hue that colours the icon, such as a kind's. */
10
+ hue?: number;
11
+ }
12
+ interface LoadedOptionSelectProps<Value extends string> extends Omit<SelectProps, 'value' | 'defaultValue' | 'onValueChange' | 'onChange' | 'children' | 'className' | 'size' | 'variant'> {
13
+ /** The select's accessible name; leave it out when a label names the select by its `id`. */
14
+ label?: string;
4
15
  loading?: false;
5
16
  onChange: (value: Value) => void;
6
- /** Disabled options stay visible but cannot be chosen. */
7
- options: readonly {
8
- label: string;
9
- value: Value;
10
- disabled?: boolean;
11
- }[];
12
- value: Value;
17
+ options: readonly OptionSelectOption<Value>[];
18
+ /** An empty value shows the placeholder, or `none`. */
19
+ value: Value | '';
20
+ /** The name of the option that empties the value, which `onChange` hears as ''. */
21
+ none?: string;
22
+ /** A form field's name: the select gives its form the value in a hidden field, and fires input on that field once
23
+ * a pick has rendered, as Select is a button over a listbox that fires no input event of its own. */
24
+ name?: string;
25
+ /** A pick writes at once, so a letter key on the closed control doesn't pick: the Select's typeahead only works
26
+ * once its list is open. */
27
+ writesOnPick?: boolean;
28
+ /** Busy rather than disabled while a pick it made saves: it keeps the focus and ignores picks. */
29
+ busy?: boolean;
30
+ /** `setting` is a settings row's 11rem dropdown; a `field` fills its container. */
31
+ presentation?: 'field' | 'setting';
32
+ ref?: Ref<HTMLButtonElement>;
13
33
  }
14
34
  interface LoadingOptionSelectProps {
15
35
  label: string;
16
36
  loading: true;
17
37
  }
18
38
  type OptionSelectProps<Value extends string> = LoadedOptionSelectProps<Value> | LoadingOptionSelectProps;
39
+ /** A Select over plain options, each with an optional icon; while they load, a control-shaped skeleton. */
19
40
  export declare function OptionSelect<Value extends string>(props: OptionSelectProps<Value>): import("react").JSX.Element;
20
41
  export {};
21
42
  //# sourceMappingURL=OptionSelect.d.ts.map
@@ -1,27 +1,67 @@
1
1
  import { SkeletonLoader } from "../feedback/SkeletonLoader.js";
2
2
  import { Select, SelectItem } from "./Select.js";
3
- import { jsx } from "react/jsx-runtime";
3
+ import { useRef } from "react";
4
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
+ import { flushSync } from "react-dom";
4
6
  // src/forms/OptionSelect.tsx
7
+ const NONE = "__none";
8
+ /** Marks a letter key as handled, which the Select's trigger takes as leaving its typeahead out. Space and arrows still
9
+ * open the list. */
10
+ const withoutClosedTypeahead = (event) => {
11
+ if (event.key.length === 1 && event.key !== " " && !event.ctrlKey && !event.altKey && !event.metaKey) event.preventDefault();
12
+ };
13
+ /** A Select over plain options, each with an optional icon; while they load, a control-shaped skeleton. */
5
14
  function OptionSelect(props) {
15
+ const field = useRef(null);
6
16
  if (props.loading) return /* @__PURE__ */ jsx(SkeletonLoader, {
7
17
  label: props.label,
8
18
  children: /* @__PURE__ */ jsx(SkeletonLoader.Shape, { presentation: "control" })
9
19
  });
10
- const { disabled, label, onChange, options, value } = props;
11
- return /* @__PURE__ */ jsx(Select, {
12
- "aria-label": label,
13
- disabled,
14
- value,
15
- onValueChange: (next) => {
16
- const option = options.find((candidate) => candidate.value === next);
17
- if (option) onChange(option.value);
18
- },
19
- children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
20
+ const { label, loading: _loading, onChange, options, value, none, name, writesOnPick, busy = false, presentation = "field", ...select } = props;
21
+ const pick = (next) => {
22
+ if (busy) return;
23
+ if (none && next === NONE) {
24
+ change(() => onChange(""));
25
+ return;
26
+ }
27
+ const option = options.find((candidate) => candidate.value === next);
28
+ if (option) change(() => onChange(option.value));
29
+ };
30
+ const change = (write) => {
31
+ if (!name) return write();
32
+ flushSync(write);
33
+ field.current?.dispatchEvent(new Event("input", { bubbles: true }));
34
+ };
35
+ const control = /* @__PURE__ */ jsxs(Fragment$1, { children: [name && /* @__PURE__ */ jsx("input", {
36
+ ref: field,
37
+ type: "hidden",
38
+ name,
39
+ value
40
+ }), /* @__PURE__ */ jsxs(Select, {
41
+ ...select,
42
+ ...label !== void 0 && { "aria-label": label },
43
+ ...writesOnPick && { onKeyDown: withoutClosedTypeahead },
44
+ ...busy && { "aria-disabled": true },
45
+ value: value || (none ? NONE : ""),
46
+ onValueChange: pick,
47
+ children: [none && /* @__PURE__ */ jsx(SelectItem, {
48
+ value: NONE,
49
+ children: none
50
+ }), options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
20
51
  value: option.value,
21
52
  disabled: option.disabled,
53
+ ...option.icon !== void 0 && {
54
+ icon: option.icon,
55
+ iconHue: option.hue,
56
+ textValue: option.label
57
+ },
22
58
  children: option.label
23
- }, option.value))
24
- });
59
+ }, option.value))]
60
+ })] });
61
+ return presentation === "setting" ? /* @__PURE__ */ jsx("span", {
62
+ className: "block w-44 max-w-full",
63
+ children: control
64
+ }) : control;
25
65
  }
26
66
  export { OptionSelect };
27
67
 
@@ -1 +1 @@
1
- {"version":3,"file":"OptionSelect.js","names":[],"sources":["../../src/forms/OptionSelect.tsx"],"sourcesContent":["import { SkeletonLoader } from '../feedback/SkeletonLoader';\nimport { Select, SelectItem } from './Select';\n\ninterface LoadedOptionSelectProps<Value extends string> {\n disabled?: boolean;\n label: string;\n loading?: false;\n onChange: (value: Value) => void;\n /** Disabled options stay visible but cannot be chosen. */\n options: readonly { label: string; value: Value; disabled?: boolean }[];\n value: Value;\n}\n\ninterface LoadingOptionSelectProps {\n label: string;\n loading: true;\n}\n\ntype OptionSelectProps<Value extends string> = LoadedOptionSelectProps<Value> | LoadingOptionSelectProps;\n\nexport function OptionSelect<Value extends string>(props: OptionSelectProps<Value>) {\n if (props.loading)\n return (\n <SkeletonLoader label={props.label}>\n <SkeletonLoader.Shape presentation=\"control\" />\n </SkeletonLoader>\n );\n\n const { disabled, label, onChange, options, value } = props;\n\n return (\n <Select\n aria-label={label}\n disabled={disabled}\n value={value}\n onValueChange={(next) => {\n const option = options.find((candidate) => candidate.value === next);\n\n if (option) onChange(option.value);\n }}\n >\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </SelectItem>\n ))}\n </Select>\n );\n}\n"],"mappings":";;;;AAoBA,SAAgB,aAAmC,OAAiC;CAChF,IAAI,MAAM,SACN,OACI,oBAAC,gBAAD;EAAgB,OAAO,MAAM;EACzB,UAAA,oBAAC,eAAe,OAAhB,EAAsB,cAAa,UAAW,CAAA;CAClC,CAAA;CAGxB,MAAM,EAAE,UAAU,OAAO,UAAU,SAAS,UAAU;CAEtD,OACI,oBAAC,QAAD;EACI,cAAY;EACF;EACH;EACP,gBAAgB,SAAS;GACrB,MAAM,SAAS,QAAQ,MAAM,cAAc,UAAU,UAAU,IAAI;GAEnE,IAAI,QAAQ,SAAS,OAAO,KAAK;EACrC;EAEC,UAAA,QAAQ,KAAK,WACV,oBAAC,YAAD;GAA+B,OAAO,OAAO;GAAO,UAAU,OAAO;GAChE,UAAA,OAAO;EACA,GAFK,OAAO,KAEZ,CACf;CACG,CAAA;AAEhB"}
1
+ {"version":3,"file":"OptionSelect.js","names":[],"sources":["../../src/forms/OptionSelect.tsx"],"sourcesContent":["import { type KeyboardEvent, type ReactNode, type Ref, useRef } from 'react';\nimport { flushSync } from 'react-dom';\n\nimport { SkeletonLoader } from '../feedback/SkeletonLoader';\nimport { Select, SelectItem, type SelectProps } from './Select';\n\nconst NONE = '__none';\n\nexport interface OptionSelectOption<Value extends string = string> {\n label: string;\n value: Value;\n /** Disabled options stay visible but cannot be chosen. */\n disabled?: boolean;\n icon?: ReactNode;\n /** A lar-brand app hue that colours the icon, such as a kind's. */\n hue?: number;\n}\n\ninterface LoadedOptionSelectProps<Value extends string> extends Omit<\n SelectProps,\n 'value' | 'defaultValue' | 'onValueChange' | 'onChange' | 'children' | 'className' | 'size' | 'variant'\n> {\n /** The select's accessible name; leave it out when a label names the select by its `id`. */\n label?: string;\n loading?: false;\n onChange: (value: Value) => void;\n options: readonly OptionSelectOption<Value>[];\n /** An empty value shows the placeholder, or `none`. */\n value: Value | '';\n /** The name of the option that empties the value, which `onChange` hears as ''. */\n none?: string;\n /** A form field's name: the select gives its form the value in a hidden field, and fires input on that field once\n * a pick has rendered, as Select is a button over a listbox that fires no input event of its own. */\n name?: string;\n /** A pick writes at once, so a letter key on the closed control doesn't pick: the Select's typeahead only works\n * once its list is open. */\n writesOnPick?: boolean;\n /** Busy rather than disabled while a pick it made saves: it keeps the focus and ignores picks. */\n busy?: boolean;\n /** `setting` is a settings row's 11rem dropdown; a `field` fills its container. */\n presentation?: 'field' | 'setting';\n ref?: Ref<HTMLButtonElement>;\n}\n\ninterface LoadingOptionSelectProps {\n label: string;\n loading: true;\n}\n\ntype OptionSelectProps<Value extends string> = LoadedOptionSelectProps<Value> | LoadingOptionSelectProps;\n\n/** Marks a letter key as handled, which the Select's trigger takes as leaving its typeahead out. Space and arrows still\n * open the list. */\nconst withoutClosedTypeahead = (event: KeyboardEvent) => {\n if (event.key.length === 1 && event.key !== ' ' && !event.ctrlKey && !event.altKey && !event.metaKey)\n event.preventDefault();\n};\n\n/** A Select over plain options, each with an optional icon; while they load, a control-shaped skeleton. */\nexport function OptionSelect<Value extends string>(props: OptionSelectProps<Value>) {\n const field = useRef<HTMLInputElement>(null);\n\n if (props.loading)\n return (\n <SkeletonLoader label={props.label}>\n <SkeletonLoader.Shape presentation=\"control\" />\n </SkeletonLoader>\n );\n\n const {\n label,\n loading: _loading,\n onChange,\n options,\n value,\n none,\n name,\n writesOnPick,\n busy = false,\n presentation = 'field',\n ...select\n } = props;\n const pick = (next: string) => {\n if (busy) return;\n\n if (none && next === NONE) {\n change(() => onChange('' as Value));\n\n return;\n }\n\n const option = options.find((candidate) => candidate.value === next);\n\n if (option) change(() => onChange(option.value));\n };\n const change = (write: () => void) => {\n if (!name) return write();\n\n flushSync(write);\n field.current?.dispatchEvent(new Event('input', { bubbles: true }));\n };\n\n const control = (\n <>\n {name && <input ref={field} type=\"hidden\" name={name} value={value} />}\n <Select\n {...select}\n {...(label !== undefined && { 'aria-label': label })}\n {...(writesOnPick && { onKeyDown: withoutClosedTypeahead })}\n {...(busy && { 'aria-disabled': true })}\n value={value || (none ? NONE : '')}\n onValueChange={pick}\n >\n {none && <SelectItem value={NONE}>{none}</SelectItem>}\n {options.map((option) => (\n <SelectItem\n key={option.value}\n value={option.value}\n disabled={option.disabled}\n {...(option.icon !== undefined && {\n icon: option.icon,\n iconHue: option.hue,\n textValue: option.label,\n })}\n >\n {option.label}\n </SelectItem>\n ))}\n </Select>\n </>\n );\n\n return presentation === 'setting' ? <span className=\"block w-44 max-w-full\">{control}</span> : control;\n}\n"],"mappings":";;;;;;AAMA,MAAM,OAAO;;;AA+Cb,MAAM,0BAA0B,UAAyB;CACrD,IAAI,MAAM,IAAI,WAAW,KAAK,MAAM,QAAQ,OAAO,CAAC,MAAM,WAAW,CAAC,MAAM,UAAU,CAAC,MAAM,SACzF,MAAM,eAAe;AAC7B;;AAGA,SAAgB,aAAmC,OAAiC;CAChF,MAAM,QAAQ,OAAyB,IAAI;CAE3C,IAAI,MAAM,SACN,OACI,oBAAC,gBAAD;EAAgB,OAAO,MAAM;EACzB,UAAA,oBAAC,eAAe,OAAhB,EAAsB,cAAa,UAAW,CAAA;CAClC,CAAA;CAGxB,MAAM,EACF,OACA,SAAS,UACT,UACA,SACA,OACA,MACA,MACA,cACA,OAAO,OACP,eAAe,SACf,GAAG,WACH;CACJ,MAAM,QAAQ,SAAiB;EAC3B,IAAI,MAAM;EAEV,IAAI,QAAQ,SAAS,MAAM;GACvB,aAAa,SAAS,EAAW,CAAC;GAElC;EACJ;EAEA,MAAM,SAAS,QAAQ,MAAM,cAAc,UAAU,UAAU,IAAI;EAEnE,IAAI,QAAQ,aAAa,SAAS,OAAO,KAAK,CAAC;CACnD;CACA,MAAM,UAAU,UAAsB;EAClC,IAAI,CAAC,MAAM,OAAO,MAAM;EAExB,UAAU,KAAK;EACf,MAAM,SAAS,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;CACtE;CAEA,MAAM,UACF,qBAAA,YAAA,EAAA,UAAA,CACK,QAAQ,oBAAC,SAAD;EAAO,KAAK;EAAO,MAAK;EAAe;EAAa;CAAQ,CAAA,GACrE,qBAAC,QAAD;EACI,GAAI;EACJ,GAAK,UAAU,KAAA,KAAa,EAAE,cAAc,MAAM;EAClD,GAAK,gBAAgB,EAAE,WAAW,uBAAuB;EACzD,GAAK,QAAQ,EAAE,iBAAiB,KAAK;EACrC,OAAO,UAAU,OAAO,OAAO;EAC/B,eAAe;EANnB,UAAA,CAQK,QAAQ,oBAAC,YAAD;GAAY,OAAO;GAAO,UAAA;EAAiB,CAAA,GACnD,QAAQ,KAAK,WACV,oBAAC,YAAD;GAEI,OAAO,OAAO;GACd,UAAU,OAAO;GACjB,GAAK,OAAO,SAAS,KAAA,KAAa;IAC9B,MAAM,OAAO;IACb,SAAS,OAAO;IAChB,WAAW,OAAO;GACtB;GAEC,UAAA,OAAO;EACA,GAVH,OAAO,KAUJ,CACf,CACG;CACV,CAAA,CAAA,EAAA,CAAA;CAGN,OAAO,iBAAiB,YAAY,oBAAC,QAAD;EAAM,WAAU;EAAyB,UAAA;CAAc,CAAA,IAAI;AACnG"}
@@ -1,6 +1,9 @@
1
1
  import { SelectItemProps, SelectProps } from "@ninna-ui/forms";
2
2
  type SelectProps$1 = Omit<SelectProps, 'fullWidth'>;
3
3
  declare const Select$1: import("react").ForwardRefExoticComponent<SelectProps$1 & import("react").RefAttributes<HTMLButtonElement>>;
4
- declare const SelectItem$1: import("react").ForwardRefExoticComponent<SelectItemProps & import("react").RefAttributes<HTMLDivElement>>;
4
+ /** An option of the Select; its `icon` takes `iconHue`, a lar-brand app hue at full strength, when given. */
5
+ declare const SelectItem$1: import("react").ForwardRefExoticComponent<SelectItemProps & {
6
+ iconHue?: number | undefined;
7
+ } & import("react").RefAttributes<HTMLDivElement>>;
5
8
  export { Select$1 as Select, SelectItem$1 as SelectItem, SelectProps$1 as SelectProps };
6
9
  //# sourceMappingURL=Select.d.ts.map
@@ -15,7 +15,8 @@ const Select$1 = forwardRef(({ size = formControlDefaults.size, variant = formCo
15
15
  className: `${variant === "filled" ? formControlClasses : ""} [&>span:first-child]:truncate ${className ?? ""}`
16
16
  }));
17
17
  Select$1.displayName = "Select";
18
- const SelectItem$1 = forwardRef(({ icon, children, ...props }, ref) => /* @__PURE__ */ jsx(SelectItem, {
18
+ /** An option of the Select; its `icon` takes `iconHue`, a lar-brand app hue at full strength, when given. */
19
+ const SelectItem$1 = forwardRef(({ icon, iconHue, children, ...props }, ref) => /* @__PURE__ */ jsx(SelectItem, {
19
20
  ref,
20
21
  ...props,
21
22
  children: icon ? /* @__PURE__ */ jsxs("span", {
@@ -23,6 +24,7 @@ const SelectItem$1 = forwardRef(({ icon, children, ...props }, ref) => /* @__PUR
23
24
  children: [/* @__PURE__ */ jsx("span", {
24
25
  "aria-hidden": "true",
25
26
  className: "size-4 shrink-0 [&>svg]:size-full",
27
+ style: iconHue === void 0 ? void 0 : { color: `oklch(0.62 0.15 ${iconHue})` },
26
28
  children: icon
27
29
  }), /* @__PURE__ */ jsx("span", {
28
30
  className: "truncate",