@lar-works/design-system 0.2.0 → 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 (433) 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/showcase.css +6 -0
  427. package/src/styles.css +9 -1
  428. package/src/typography/Code.tsx +24 -0
  429. package/src/typography/Link.tsx +19 -10
  430. package/src/typography/Typography.tsx +25 -2
  431. package/src/typography/index.ts +1 -1
  432. package/dist/feedback/index.js +0 -11
  433. package/dist/typography/index.js +0 -4
@@ -0,0 +1,52 @@
1
+ /* The board's role tokens. The board's own roles (--you, --thread, --sub, --build…) name what a colour means on the
2
+ board: you, a T3 thread, a sub-issue, a build. The others are the role names the board's pages read, each pointed
3
+ at a design-system token; they are plain variables, not Tailwind colours, so ninna-ui's own primary, secondary and
4
+ accent hues stay as the theme sets them. The components in ./ use the theme's tokens directly where they can. */
5
+ :root {
6
+ --radius: var(--radius-lg);
7
+ --background: var(--color-base-50);
8
+ --foreground: var(--color-base-content);
9
+ --card: var(--color-base-100);
10
+ --primary: var(--color-primary);
11
+ --secondary: var(--color-zinc-50);
12
+ --secondary-foreground: var(--foreground);
13
+ --muted: var(--color-zinc-50);
14
+ --muted-foreground: var(--color-base-700);
15
+ --accent: var(--color-base-200);
16
+ --error: var(--color-danger);
17
+ --error-foreground: var(--lar-danger-text);
18
+ --error-surface: color-mix(in srgb, var(--error) 8%, transparent);
19
+ --destructive-foreground: var(--error-foreground);
20
+ --border: var(--color-border);
21
+ --input: var(--color-base-400);
22
+ --ring: var(--primary);
23
+ --success: var(--color-success);
24
+ --success-foreground: var(--lar-success-text);
25
+ --warning: var(--color-warning);
26
+ --warning-foreground: var(--lar-warning-text);
27
+ --warning-surface: color-mix(in srgb, var(--warning) 8%, transparent);
28
+
29
+ /* The board's own roles. */
30
+ --you: var(--warning);
31
+ --thread: var(--primary);
32
+ --sub: var(--color-violet-500);
33
+ --build: var(--success);
34
+ --danger-text: var(--error-foreground);
35
+ --soft: color-mix(in srgb, var(--foreground) 7%, transparent);
36
+
37
+ /* Each status's colour, which StatusGlyph, the stage track, the slice and part bars draw in. */
38
+ --st-amber: var(--you);
39
+ --st-now: var(--thread);
40
+ --st-review: var(--sub);
41
+ --st-done: var(--build);
42
+ --st-idle: color-mix(in srgb, var(--muted-foreground) 70%, transparent);
43
+
44
+ @variant dark {
45
+ --secondary: --alpha(var(--color-white) / 3%);
46
+ --muted: --alpha(var(--color-white) / 3%);
47
+ --error-surface: color-mix(in srgb, var(--error) 16%, transparent);
48
+ --warning-surface: color-mix(in srgb, var(--warning) 16%, transparent);
49
+
50
+ --sub: var(--color-violet-400);
51
+ }
52
+ }
package/src/board.css ADDED
@@ -0,0 +1,19 @@
1
+ /* styles.css plus Lar Factory's board (its role tokens, base styles, title bar, loading screen, button kinds, item parts
2
+ and rows): the import for the board app. It scans every source, so the generic components the board composes are
3
+ there too. */
4
+ @import './foundations/theme.css';
5
+ @import './board/tokens.css';
6
+ @import './board/base.css';
7
+ @import './board/title-bar.css';
8
+ @import './board/loading-screen.css';
9
+ @import './board/button-kinds.css';
10
+ @import './board/items.css';
11
+ @import './board/rows.css';
12
+
13
+ @source './';
14
+
15
+ /* ninna-ui's own @source paths assume npm's flat node_modules and find nothing under pnpm, so its components'
16
+ classes are scanned from here: next to this package once installed (pnpm's store or npm's tree), and in this
17
+ package's own node_modules inside the lar-web workspace. */
18
+ @source '../../../@ninna-ui/*/dist/**/*.js';
19
+ @source '../node_modules/@ninna-ui/*/dist/**/*.js';
@@ -13,9 +13,14 @@ const variants = {
13
13
  compact: { layout: 'max-w-64 px-3 py-6', icon: '[&>svg]:size-6', space: 'related' },
14
14
  /** A quiet empty state with the same surface as dashboard content. */
15
15
  panel: { layout: 'max-w-md px-4 py-5', icon: '[&>svg]:size-6', space: 'related' },
16
+ /** A first run's welcome in an app's empty main area: its `icon` (such as the brand mark) in its own colours over a
17
+ * section-sized title. */
18
+ welcome: { layout: 'max-w-xl px-6 py-16', icon: '', space: 'group' },
16
19
  } as const;
17
20
 
18
21
  interface CollectionStateProps {
22
+ /** The action in place of `actionLabel` and `onAction`, for a caller whose button has its own look or markers. */
23
+ action?: ReactNode;
19
24
  actionLabel?: string;
20
25
  description: string;
21
26
  icon?: ReactNode;
@@ -25,6 +30,7 @@ interface CollectionStateProps {
25
30
  }
26
31
 
27
32
  export function CollectionState({
33
+ action,
28
34
  actionLabel,
29
35
  description,
30
36
  icon,
@@ -39,12 +45,18 @@ export function CollectionState({
39
45
  space={variants[variant].space}
40
46
  className={`mx-auto text-center ${variants[variant].layout}`}
41
47
  >
42
- {icon ? <span className={`text-base-500 ${variants[variant].icon}`}>{icon}</span> : null}
48
+ {icon ? (
49
+ variant === 'welcome' ? (
50
+ icon
51
+ ) : (
52
+ <span className={`text-base-500 ${variants[variant].icon}`}>{icon}</span>
53
+ )
54
+ ) : null}
43
55
  <Stack space="tight">
44
56
  {compact ? (
45
57
  <Text variant="label">{title}</Text>
46
58
  ) : (
47
- <Heading as="h2" variant="subsection">
59
+ <Heading as="h2" variant={variant === 'welcome' ? 'section' : 'subsection'}>
48
60
  {title}
49
61
  </Heading>
50
62
  )}
@@ -52,11 +64,12 @@ export function CollectionState({
52
64
  {description}
53
65
  </Text>
54
66
  </Stack>
55
- {actionLabel && onAction ? (
56
- <Button type="button" onClick={onAction} variant="secondary">
57
- {actionLabel}
58
- </Button>
59
- ) : null}
67
+ {action ??
68
+ (actionLabel && onAction ? (
69
+ <Button type="button" onClick={onAction} variant="secondary">
70
+ {actionLabel}
71
+ </Button>
72
+ ) : null)}
60
73
  </Stack>
61
74
  );
62
75
 
@@ -1,7 +1,10 @@
1
1
  import { Avatar as NinnaAvatar, type AvatarProps as NinnaAvatarProps } from '@ninna-ui/primitives';
2
2
 
3
- type AvatarProps = Omit<NinnaAvatarProps, 'color' | 'size' | 'radius'>;
3
+ type AvatarProps = Omit<NinnaAvatarProps, 'color' | 'size' | 'radius'> & {
4
+ /** The 24px avatar beside a name in a dense list, such as a comment's author. */
5
+ compact?: boolean;
6
+ };
4
7
 
5
- export function Avatar(props: AvatarProps) {
6
- return <NinnaAvatar {...props} color="neutral" size="sm" />;
8
+ export function Avatar({ compact = false, ...props }: AvatarProps) {
9
+ return <NinnaAvatar {...props} color="neutral" size={compact ? 'xs' : 'sm'} />;
7
10
  }
@@ -2,11 +2,17 @@ import { Badge as NinnaBadge, type BadgeProps as NinnaBadgeProps } from '@ninna-
2
2
  import type { CSSProperties } from 'react';
3
3
 
4
4
  import type { StatusTone } from '../feedback/StatusIndicator';
5
+ import { tint } from './LabelTint';
5
6
 
6
7
  export type BadgeProps = Omit<NinnaBadgeProps, 'color' | 'size' | 'variant'> & {
7
8
  tone?: StatusTone | 'accent';
8
9
  /** A lar-brand app hue that colours the badge in place of its tone. */
9
10
  hue?: number;
11
+ /** A tracker label's own colour, such as GitHub's, as a soft tint readable in both modes; anything but a colour
12
+ * keeps the tone. */
13
+ labelColor?: string | undefined;
14
+ /** Dims the text to the muted colour, for an entry that is listed but switched off. */
15
+ muted?: boolean;
10
16
  };
11
17
 
12
18
  /** T3 Code's badge: a 6px-radius label on an 8% tint of its tone, in the tone's foreground colour. */
@@ -19,15 +25,31 @@ const toneClasses: Record<NonNullable<BadgeProps['tone']>, string> = {
19
25
  info: 'bg-info/10 text-(--lar-info-text) dark:bg-info/16',
20
26
  };
21
27
 
22
- export function Badge({ tone = 'neutral', hue, className = '', style, ...props }: BadgeProps) {
28
+ export function Badge({
29
+ tone = 'neutral',
30
+ hue,
31
+ labelColor,
32
+ muted = false,
33
+ className = '',
34
+ style,
35
+ ...props
36
+ }: BadgeProps) {
37
+ const labelTint = tint(labelColor);
38
+
23
39
  return (
24
40
  <NinnaBadge
25
41
  {...props}
26
42
  color={tone === 'accent' ? 'primary' : tone}
27
43
  size="sm"
28
44
  variant="soft"
29
- style={hue === undefined ? style : ({ ...style, '--lar-kind-hue': hue } as CSSProperties)}
30
- className={`h-5 gap-1 rounded-sm px-1.5 text-xs font-medium [&>svg]:size-3 [&>svg]:shrink-0 ${hue === undefined ? toneClasses[tone] : 'kind-badge!'} ${className}`}
45
+ style={
46
+ hue !== undefined
47
+ ? ({ ...style, '--lar-kind-hue': hue } as CSSProperties)
48
+ : labelTint
49
+ ? { ...style, ...labelTint }
50
+ : style
51
+ }
52
+ className={`h-5 gap-1 rounded-sm px-1.5 text-xs font-medium [&>svg]:size-3 [&>svg]:shrink-0 ${hue === undefined ? toneClasses[tone] : 'kind-badge!'} ${muted ? 'text-(--muted-foreground)' : ''} ${className}`}
31
53
  />
32
54
  );
33
55
  }
@@ -0,0 +1,26 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import { Avatar } from './Avatar';
4
+
5
+ /** The comments, newest first, one Comment each, such as a ticket's. */
6
+ export function CommentList({ children }: { children: ReactNode }) {
7
+ return <ol className="m-0 grid list-none grid-cols-[minmax(0,1fr)] gap-3 p-0">{children}</ol>;
8
+ }
9
+
10
+ /** A comment: its author's avatar beside a card with their name and `time` over the comment. */
11
+ export function Comment({ author, time, children }: { author: string; time: ReactNode; children: ReactNode }) {
12
+ return (
13
+ <li className="flex gap-3">
14
+ <Avatar name={author} compact />
15
+ <div className="border-border bg-base-100 min-w-0 flex-1 rounded-lg border">
16
+ <div className="border-border flex items-center gap-2 border-b px-3 py-1.5 text-xs">
17
+ <span className="font-medium" data-comment-author>
18
+ {author}
19
+ </span>
20
+ <span className="text-base-700">{time}</span>
21
+ </div>
22
+ <div className="px-3 py-2">{children}</div>
23
+ </div>
24
+ </li>
25
+ );
26
+ }
@@ -1,6 +1,7 @@
1
1
  import { CheckIcon, CopyIcon } from 'lucide-react';
2
2
  import { useEffect, useState } from 'react';
3
3
 
4
+ import { Button } from '../actions/Button';
4
5
  import { IconButton } from '../actions/IconButton';
5
6
 
6
7
  const COPIED_FEEDBACK_MS = 1500;
@@ -9,10 +10,25 @@ interface CopyButtonProps {
9
10
  copiedLabel: string;
10
11
  label: string;
11
12
  value: string;
13
+ /** `icon` is a copy icon that turns into a tick; `text` is a compact button that reads `copyText`, then
14
+ * `copiedLabel`, as a dense tool's Copy beside a command. */
15
+ presentation?: 'icon' | 'text';
16
+ /** The text presentation's word before a copy, such as "Copy"; `label` stays its accessible name. */
17
+ copyText?: string;
18
+ /** Called after each copy, such as to announce it in a live region. */
19
+ onCopied?: () => void;
12
20
  }
13
21
 
14
- /** The LAN admin also runs over HTTP, where the async clipboard API is unavailable. */
15
- export function CopyButton({ copiedLabel, label, value }: CopyButtonProps) {
22
+ /** Copies `value`. The LAN admin also runs over HTTP, where the async clipboard API is unavailable, so it falls back
23
+ * to a selected textarea. */
24
+ export function CopyButton({
25
+ copiedLabel,
26
+ label,
27
+ value,
28
+ presentation = 'icon',
29
+ copyText = label,
30
+ onCopied,
31
+ }: CopyButtonProps) {
16
32
  const [copiedValue, setCopiedValue] = useState<string | null>(null);
17
33
  const [copying, setCopying] = useState(false);
18
34
  const copied = copiedValue === value;
@@ -25,28 +41,42 @@ export function CopyButton({ copiedLabel, label, value }: CopyButtonProps) {
25
41
  return () => window.clearTimeout(timer);
26
42
  }, [copiedValue]);
27
43
 
44
+ const copy = async () => {
45
+ setCopying(true);
46
+
47
+ let done = false;
48
+
49
+ try {
50
+ if (navigator.clipboard) {
51
+ await navigator.clipboard.writeText(value);
52
+ done = true;
53
+ } else {
54
+ done = copySelection(value);
55
+ }
56
+ } catch {
57
+ done = copySelection(value);
58
+ } finally {
59
+ setCopiedValue(done ? value : null);
60
+ setCopying(false);
61
+ }
62
+
63
+ if (done) onCopied?.();
64
+ };
65
+
66
+ if (presentation === 'text')
67
+ return (
68
+ <Button variant="secondary" compact aria-label={label} onClick={() => void copy()}>
69
+ {copied ? copiedLabel : copyText}
70
+ </Button>
71
+ );
72
+
28
73
  return (
29
74
  <IconButton
30
75
  icon={copied ? <CheckIcon /> : <CopyIcon />}
31
76
  aria-label={copied ? copiedLabel : label}
32
77
  title={copied ? copiedLabel : label}
33
78
  loading={copying}
34
- onClick={async () => {
35
- setCopying(true);
36
-
37
- try {
38
- if (navigator.clipboard) {
39
- await navigator.clipboard.writeText(value);
40
- setCopiedValue(value);
41
- } else {
42
- setCopiedValue(copySelection(value) ? value : null);
43
- }
44
- } catch {
45
- setCopiedValue(copySelection(value) ? value : null);
46
- } finally {
47
- setCopying(false);
48
- }
49
- }}
79
+ onClick={copy}
50
80
  tone={copied ? 'success' : 'neutral'}
51
81
  />
52
82
  );
@@ -0,0 +1,59 @@
1
+ import { useEffect, useId, useRef, useState } from 'react';
2
+
3
+ interface DiagramProps {
4
+ /** The diagram's text, such as a Mermaid flowchart. */
5
+ source: string;
6
+ /** Draws `source` as an SVG document's markup; `id` is unique to this diagram. A rejection shows `failedLabel`. */
7
+ draw: (source: string, id: string) => Promise<string>;
8
+ /** The drawing's accessible name. */
9
+ label: string;
10
+ /** Shown in place of the drawing when `draw` fails. */
11
+ failedLabel: string;
12
+ /** The disclosure that shows the source under the drawing. */
13
+ sourceLabel: string;
14
+ }
15
+
16
+ /** A drawn diagram in a bordered figure, with its source under it in a disclosure. The SVG comes from `draw`, so the
17
+ * design system needs no diagram library; a drawing too wide for its figure scrolls inside it. */
18
+ export function Diagram({ source, draw, label, failedLabel, sourceLabel }: DiagramProps) {
19
+ const id = useId().replace(/[^a-zA-Z0-9]/g, '');
20
+ const host = useRef<HTMLDivElement>(null);
21
+ // The source the diagram failed on, so a new source draws afresh.
22
+ const [failed, setFailed] = useState<string | null>(null);
23
+
24
+ useEffect(() => {
25
+ const drawn = new AbortController();
26
+
27
+ draw(source, id).then(
28
+ (svg) => {
29
+ if (!drawn.signal.aborted && host.current) host.current.innerHTML = svg;
30
+ },
31
+ () => {
32
+ if (!drawn.signal.aborted) setFailed(source);
33
+ },
34
+ );
35
+
36
+ return () => drawn.abort();
37
+ }, [draw, id, source]);
38
+
39
+ return (
40
+ <figure className="m-0 grid min-w-0 gap-2 rounded-lg border p-3" data-map-diagram>
41
+ {failed === source ? (
42
+ <p role="status" className="text-base-700 m-0 text-xs">
43
+ {failedLabel}
44
+ </p>
45
+ ) : (
46
+ <div
47
+ ref={host}
48
+ className="overflow-auto [&_svg]:mx-auto [&_svg]:h-auto [&_svg]:max-w-full"
49
+ role="img"
50
+ aria-label={label}
51
+ />
52
+ )}
53
+ <details className="text-base-700 text-xs">
54
+ <summary className="cursor-pointer">{sourceLabel}</summary>
55
+ <pre className="overflow-auto pt-2 whitespace-pre-wrap">{source}</pre>
56
+ </details>
57
+ </figure>
58
+ );
59
+ }
@@ -7,17 +7,20 @@ interface InsetPanelProps {
7
7
  children: ReactNode;
8
8
  onClick?: () => void;
9
9
  presentation?: 'default' | 'settings';
10
+ /** Under a `settings` panel's rows, on the muted surface: the group's own action, such as an add button. */
11
+ footer?: ReactNode;
10
12
  }
11
13
 
12
14
  /**
13
15
  * A neutral surface that groups related content without adding message or status meaning.
14
16
  * Use Alert for current status, InfoBox for optional guidance, and NoticeCard for a decision item.
15
17
  */
16
- export function InsetPanel({ children, onClick, presentation = 'default' }: InsetPanelProps) {
18
+ export function InsetPanel({ children, onClick, presentation = 'default', footer }: InsetPanelProps) {
17
19
  if (presentation === 'settings') {
18
20
  return (
19
21
  <div className="divide-base-300 border-base-300 bg-base-100 divide-y overflow-hidden rounded-lg border shadow-xs/5">
20
22
  {children}
23
+ {footer && <div className="rounded-b-xl bg-(--muted) px-4 py-2">{footer}</div>}
21
24
  </div>
22
25
  );
23
26
  }
@@ -0,0 +1,73 @@
1
+ import { LockIcon } from 'lucide-react';
2
+ import type { ReactNode } from 'react';
3
+
4
+ import { Badge } from './Badge';
5
+
6
+ type DataAttributes = { [key: `data-${string}`]: string | undefined };
7
+
8
+ interface LabelChipProps extends DataAttributes {
9
+ /** The label's name, truncating, which its tooltip gives whole. */
10
+ label: string;
11
+ /** The label's own colour, such as a tracker's, as Badge's `labelColor`; none keeps the neutral badge. */
12
+ color?: string | undefined;
13
+ /** Follows the name, faded, such as "new" for a label still to be created. */
14
+ note?: ReactNode;
15
+ /** A lock before the name, for a label that can't be edited here; `lockedLabel` says why. */
16
+ locked?: boolean;
17
+ lockedLabel?: string;
18
+ /** A × after the name, for a chip inside a control that removes it, such as an active filter's. */
19
+ removable?: boolean;
20
+ /** Its own × button, which `removeLabel` names, that removes the label. */
21
+ onRemove?: () => void;
22
+ removeLabel?: string;
23
+ disabled?: boolean;
24
+ }
25
+
26
+ /** A label as a chip in its own colour, such as a tracker's: its name truncating, then any note, and a lock or a × that
27
+ * removes it. */
28
+ export function LabelChip({
29
+ label,
30
+ color,
31
+ note,
32
+ locked = false,
33
+ lockedLabel,
34
+ removable = false,
35
+ onRemove,
36
+ removeLabel,
37
+ disabled = false,
38
+ ...data
39
+ }: LabelChipProps) {
40
+ return (
41
+ <Badge
42
+ data-label-chip=""
43
+ {...data}
44
+ title={locked ? lockedLabel : undefined}
45
+ className="max-w-full min-w-0"
46
+ labelColor={color}
47
+ >
48
+ {locked && <LockIcon aria-label={lockedLabel} />}
49
+ <span className="max-w-52 truncate" title={label}>
50
+ {label}
51
+ </span>
52
+ {note && (
53
+ <>
54
+ {' '}
55
+ <span className="opacity-70">{note}</span>
56
+ </>
57
+ )}
58
+ {onRemove ? (
59
+ <button
60
+ type="button"
61
+ className="cursor-pointer bg-transparent p-0"
62
+ aria-label={removeLabel}
63
+ disabled={disabled}
64
+ onClick={onRemove}
65
+ >
66
+ ×
67
+ </button>
68
+ ) : (
69
+ removable && <span aria-hidden="true">×</span>
70
+ )}
71
+ </Badge>
72
+ );
73
+ }
@@ -0,0 +1,72 @@
1
+ // A tracker's label colour (GitHub's, say) as a chip's style. A label's colour says nothing about what it sits on, so the text starts from a
2
+ // mix with the foreground and moves towards the foreground, in each mode, until it reaches the contrast a chip's name needs.
3
+
4
+ type Rgb = readonly [number, number, number];
5
+ type Mode = 'light' | 'dark';
6
+
7
+ /** WCAG AA for text this small. */
8
+ export const CONTRAST_FLOOR = 4.5;
9
+
10
+ /** The palette's foreground, and the surface a chip's text contrasts least with: the accent a hovered or picked row fills with. */
11
+ export const PALETTE: Record<Mode, { foreground: string; surface: string }> = {
12
+ light: { foreground: '#27272a', surface: '#f4f4f5' },
13
+ dark: { foreground: '#f5f5f5', surface: '#161616' },
14
+ };
15
+
16
+ const FILL = 0.16;
17
+ const TEXT_SHARE = 0.55;
18
+
19
+ const rgbOf = (hex: string): Rgb | null => {
20
+ const digits = /^#?([0-9a-f]{6})$/i.exec(hex)?.[1];
21
+
22
+ return digits ? ([0, 2, 4].map((at) => parseInt(digits.slice(at, at + 2), 16)) as unknown as Rgb) : null;
23
+ };
24
+ const mixed = (a: Rgb, b: Rgb, share: number): Rgb =>
25
+ a.map((part, at) => part * share + b[at]! * (1 - share)) as unknown as Rgb;
26
+ const hexOf = (color: Rgb) => `#${color.map((part) => Math.round(part).toString(16).padStart(2, '0')).join('')}`;
27
+
28
+ const luminance = (color: Rgb): number => {
29
+ const [r, g, b] = color.map((part) => {
30
+ const linear = part / 255;
31
+
32
+ return linear <= 0.03928 ? linear / 12.92 : ((linear + 0.055) / 1.055) ** 2.4;
33
+ }) as unknown as Rgb;
34
+
35
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
36
+ };
37
+
38
+ /** The WCAG contrast ratio of two colours. */
39
+ export function contrastRatio(a: string, b: string): number {
40
+ const [lighter, darker] = [luminance(rgbOf(a)!), luminance(rgbOf(b)!)].sort((x, y) => y - x) as [number, number];
41
+
42
+ return (lighter + 0.05) / (darker + 0.05);
43
+ }
44
+
45
+ /** A chip's fill in `mode`, as tint's `color-mix` paints it over the palette's surface. */
46
+ export const chipFill = (color: string, mode: Mode): string =>
47
+ hexOf(mixed(rgbOf(color)!, rgbOf(PALETTE[mode].surface)!, FILL));
48
+
49
+ /** A chip's text in `mode`: as much of the label's colour as the contrast floor allows. The foreground alone always clears it. */
50
+ export function chipText(color: string, mode: Mode): string {
51
+ const label = rgbOf(color)!;
52
+ const foreground = rgbOf(PALETTE[mode].foreground)!;
53
+ const fill = chipFill(color, mode);
54
+
55
+ for (let share = TEXT_SHARE; share > 0; share -= 0.05) {
56
+ const text = hexOf(mixed(label, foreground, share));
57
+
58
+ if (contrastRatio(text, fill) >= CONTRAST_FLOOR) return text;
59
+ }
60
+
61
+ return PALETTE[mode].foreground;
62
+ }
63
+
64
+ /** A soft tint of a tracker's label colour with its name readable in both modes; none, or one that isn't a colour, keeps the Badge's neutral style. */
65
+ export const tint = (color: string | undefined) =>
66
+ color && rgbOf(color)
67
+ ? {
68
+ backgroundColor: `color-mix(in srgb, ${color} ${FILL * 100}%, transparent)`,
69
+ color: `light-dark(${chipText(color, 'light')}, ${chipText(color, 'dark')})`,
70
+ boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${color} 40%, transparent)`,
71
+ }
72
+ : undefined;
@@ -0,0 +1,11 @@
1
+ import { Avatar } from './Avatar';
2
+
3
+ /** A person: their avatar and name, the name wrapping. */
4
+ export function Person({ name }: { name: string }) {
5
+ return (
6
+ <span className="inline-flex max-w-full min-w-0 items-center gap-1.5">
7
+ <Avatar name={name} compact />
8
+ <span className="min-w-0 wrap-anywhere">{name}</span>
9
+ </span>
10
+ );
11
+ }
@@ -2,21 +2,30 @@ import type { ReactNode } from 'react';
2
2
 
3
3
  import { tabbable } from '../foundations/KeyboardFocus';
4
4
 
5
+ type DataAttributes = { [key: `data-${string}`]: string | undefined };
6
+
7
+ /** One choice of a group of radios, a row of an InsetPanel's `settings` panel: the radio and its content, which can
8
+ * shrink so a long title in it truncates, then any `trailing` content. Data attributes, such as the value it names,
9
+ * go on the row. */
5
10
  export function RadioChoice({
6
11
  name,
7
12
  checked,
8
13
  onChange,
9
14
  children,
10
15
  trailing,
16
+ ...data
11
17
  }: {
12
18
  name: string;
13
19
  checked: boolean;
14
20
  onChange: () => void;
15
21
  children: ReactNode;
16
22
  trailing?: ReactNode;
17
- }) {
23
+ } & DataAttributes) {
18
24
  return (
19
- <div className="hover:bg-base-100 has-checked:bg-primary/10 has-focus-visible:ring-primary flex min-w-0 items-center gap-3 transition-colors has-focus-visible:ring-2 has-focus-visible:ring-inset has-disabled:opacity-60">
25
+ <div
26
+ {...data}
27
+ className="hover:bg-base-100 has-checked:bg-primary/10 has-focus-visible:ring-primary flex min-w-0 items-center gap-3 transition-colors has-focus-visible:ring-2 has-focus-visible:ring-inset has-disabled:opacity-60"
28
+ >
20
29
  <label className="flex min-w-0 flex-1 cursor-pointer items-center gap-3 px-4 py-3 has-disabled:cursor-default">
21
30
  <input
22
31
  {...tabbable}