react-cheminfo 0.38.0 → 0.39.1

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 (327) hide show
  1. package/lib/chart/ui/Axes.d.ts +19 -0
  2. package/lib/chart/ui/Axes.d.ts.map +1 -1
  3. package/lib/chart/ui/Axes.js +25 -5
  4. package/lib/chart/ui/Axes.js.map +1 -1
  5. package/lib/chrome/ui/NavMenuButton.d.ts.map +1 -1
  6. package/lib/chrome/ui/NavMenuButton.js +41 -11
  7. package/lib/chrome/ui/NavMenuButton.js.map +1 -1
  8. package/lib/chrome/ui/SiteHeader.js +24 -1
  9. package/lib/chrome/ui/SiteHeader.js.map +1 -1
  10. package/lib/chrome/ui/navItem.d.ts +7 -0
  11. package/lib/chrome/ui/navItem.d.ts.map +1 -1
  12. package/lib/chrome/ui/navItem.js.map +1 -1
  13. package/lib/citation/ui/entries.d.ts.map +1 -1
  14. package/lib/citation/ui/entries.js +6 -1
  15. package/lib/citation/ui/entries.js.map +1 -1
  16. package/lib/clipboard/ui/ClickToCopy.d.ts +11 -0
  17. package/lib/clipboard/ui/ClickToCopy.d.ts.map +1 -1
  18. package/lib/clipboard/ui/ClickToCopy.js +28 -10
  19. package/lib/clipboard/ui/ClickToCopy.js.map +1 -1
  20. package/lib/clipboard/ui/CopyButton.d.ts +2 -1
  21. package/lib/clipboard/ui/CopyButton.d.ts.map +1 -1
  22. package/lib/clipboard/ui/CopyButton.js +2 -1
  23. package/lib/clipboard/ui/CopyButton.js.map +1 -1
  24. package/lib/clipboard/ui/CopyableValue.d.ts +24 -4
  25. package/lib/clipboard/ui/CopyableValue.d.ts.map +1 -1
  26. package/lib/clipboard/ui/CopyableValue.js +35 -3
  27. package/lib/clipboard/ui/CopyableValue.js.map +1 -1
  28. package/lib/clipboard/ui/index.d.ts +1 -1
  29. package/lib/clipboard/ui/index.d.ts.map +1 -1
  30. package/lib/clipboard/ui/index.js.map +1 -1
  31. package/lib/confirm/ui/ConfirmButton.d.ts.map +1 -1
  32. package/lib/confirm/ui/ConfirmButton.js +2 -1
  33. package/lib/confirm/ui/ConfirmButton.js.map +1 -1
  34. package/lib/delimited/ui/TableDataButton.d.ts.map +1 -1
  35. package/lib/delimited/ui/TableDataButton.js +2 -1
  36. package/lib/delimited/ui/TableDataButton.js.map +1 -1
  37. package/lib/download/core/figureSvg.d.ts.map +1 -1
  38. package/lib/download/core/figureSvg.js +37 -12
  39. package/lib/download/core/figureSvg.js.map +1 -1
  40. package/lib/help/ui/HelpToolbarButton.d.ts.map +1 -1
  41. package/lib/help/ui/HelpToolbarButton.js +2 -1
  42. package/lib/help/ui/HelpToolbarButton.js.map +1 -1
  43. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  44. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  45. package/lib/locales/de.json +39 -4
  46. package/lib/locales/en.json +39 -4
  47. package/lib/locales/es.json +39 -4
  48. package/lib/locales/fr.json +39 -4
  49. package/lib/locales/it.json +39 -4
  50. package/lib/number/ui/NumberInput.js +1 -1
  51. package/lib/number/ui/NumberInput.js.map +1 -1
  52. package/lib/overlay/ui/OverlayIconButton.d.ts.map +1 -1
  53. package/lib/overlay/ui/OverlayIconButton.js +1 -1
  54. package/lib/overlay/ui/OverlayIconButton.js.map +1 -1
  55. package/lib/panel/ui/ClearButton.d.ts.map +1 -1
  56. package/lib/panel/ui/ClearButton.js +1 -1
  57. package/lib/panel/ui/ClearButton.js.map +1 -1
  58. package/lib/pedagogy/core/inlineMarks.d.ts +9 -5
  59. package/lib/pedagogy/core/inlineMarks.d.ts.map +1 -1
  60. package/lib/pedagogy/core/inlineMarks.js +14 -6
  61. package/lib/pedagogy/core/inlineMarks.js.map +1 -1
  62. package/lib/pedagogy/ui/GlossaryDefinition.d.ts +4 -10
  63. package/lib/pedagogy/ui/GlossaryDefinition.d.ts.map +1 -1
  64. package/lib/pedagogy/ui/GlossaryDefinition.js +7 -7
  65. package/lib/pedagogy/ui/GlossaryDefinition.js.map +1 -1
  66. package/lib/pedagogy/ui/GlossaryIndex.d.ts.map +1 -1
  67. package/lib/pedagogy/ui/GlossaryIndex.js +1 -1
  68. package/lib/pedagogy/ui/GlossaryIndex.js.map +1 -1
  69. package/lib/pedagogy/ui/GlossaryProvider.d.ts +7 -0
  70. package/lib/pedagogy/ui/GlossaryProvider.d.ts.map +1 -1
  71. package/lib/pedagogy/ui/GlossaryProvider.js +3 -2
  72. package/lib/pedagogy/ui/GlossaryProvider.js.map +1 -1
  73. package/lib/pedagogy/ui/GlossaryText.d.ts +7 -2
  74. package/lib/pedagogy/ui/GlossaryText.d.ts.map +1 -1
  75. package/lib/pedagogy/ui/GlossaryText.js +3 -2
  76. package/lib/pedagogy/ui/GlossaryText.js.map +1 -1
  77. package/lib/pedagogy/ui/InlineText.d.ts +8 -2
  78. package/lib/pedagogy/ui/InlineText.d.ts.map +1 -1
  79. package/lib/pedagogy/ui/InlineText.js +7 -2
  80. package/lib/pedagogy/ui/InlineText.js.map +1 -1
  81. package/lib/pedagogy/ui/ReferenceSectionBlock.d.ts +4 -1
  82. package/lib/pedagogy/ui/ReferenceSectionBlock.d.ts.map +1 -1
  83. package/lib/pedagogy/ui/ReferenceSectionBlock.js +2 -1
  84. package/lib/pedagogy/ui/ReferenceSectionBlock.js.map +1 -1
  85. package/lib/pedagogy/ui/SyntaxTooltip.d.ts.map +1 -1
  86. package/lib/pedagogy/ui/SyntaxTooltip.js +11 -10
  87. package/lib/pedagogy/ui/SyntaxTooltip.js.map +1 -1
  88. package/lib/pedagogy/ui/TutorialStepStrip.d.ts.map +1 -1
  89. package/lib/pedagogy/ui/TutorialStepStrip.js +18 -4
  90. package/lib/pedagogy/ui/TutorialStepStrip.js.map +1 -1
  91. package/lib/pedagogy/ui/glossaryContext.d.ts +7 -0
  92. package/lib/pedagogy/ui/glossaryContext.d.ts.map +1 -1
  93. package/lib/pedagogy/ui/glossaryContext.js.map +1 -1
  94. package/lib/pedagogy/ui/index.d.ts +1 -1
  95. package/lib/pedagogy/ui/index.d.ts.map +1 -1
  96. package/lib/pedagogy/ui/inlineSegments.d.ts +5 -0
  97. package/lib/pedagogy/ui/inlineSegments.d.ts.map +1 -1
  98. package/lib/pedagogy/ui/inlineSegments.js +2 -0
  99. package/lib/pedagogy/ui/inlineSegments.js.map +1 -1
  100. package/lib/pedagogy/ui/pedagogyStyle.d.ts +8 -9
  101. package/lib/pedagogy/ui/pedagogyStyle.d.ts.map +1 -1
  102. package/lib/pedagogy/ui/pedagogyStyle.js +10 -18
  103. package/lib/pedagogy/ui/pedagogyStyle.js.map +1 -1
  104. package/lib/seo/core/plainDescription.d.ts +2 -2
  105. package/lib/seo/core/plainDescription.js +5 -2
  106. package/lib/seo/core/plainDescription.js.map +1 -1
  107. package/lib/share/core/devices.d.ts +41 -0
  108. package/lib/share/core/devices.d.ts.map +1 -0
  109. package/lib/share/core/devices.js +58 -0
  110. package/lib/share/core/devices.js.map +1 -0
  111. package/lib/share/core/index.d.ts +4 -0
  112. package/lib/share/core/index.d.ts.map +1 -1
  113. package/lib/share/core/index.js +2 -0
  114. package/lib/share/core/index.js.map +1 -1
  115. package/lib/share/core/regions.d.ts +43 -0
  116. package/lib/share/core/regions.d.ts.map +1 -0
  117. package/lib/share/core/regions.js +71 -0
  118. package/lib/share/core/regions.js.map +1 -0
  119. package/lib/share/ui/HiddenPartsProvider.d.ts.map +1 -1
  120. package/lib/share/ui/HiddenPartsProvider.js +4 -1
  121. package/lib/share/ui/HiddenPartsProvider.js.map +1 -1
  122. package/lib/share/ui/PagePart.d.ts.map +1 -1
  123. package/lib/share/ui/PagePart.js +10 -1
  124. package/lib/share/ui/PagePart.js.map +1 -1
  125. package/lib/share/ui/ShareButton.d.ts.map +1 -1
  126. package/lib/share/ui/ShareButton.js +3 -4
  127. package/lib/share/ui/ShareButton.js.map +1 -1
  128. package/lib/share/ui/ShareConfigOptions.d.ts +6 -1
  129. package/lib/share/ui/ShareConfigOptions.d.ts.map +1 -1
  130. package/lib/share/ui/ShareConfigOptions.js +6 -13
  131. package/lib/share/ui/ShareConfigOptions.js.map +1 -1
  132. package/lib/share/ui/ShareDialog.d.ts +14 -0
  133. package/lib/share/ui/ShareDialog.d.ts.map +1 -1
  134. package/lib/share/ui/ShareDialog.js +3 -3
  135. package/lib/share/ui/ShareDialog.js.map +1 -1
  136. package/lib/share/ui/ShareDialogContent.d.ts +2 -2
  137. package/lib/share/ui/ShareDialogContent.d.ts.map +1 -1
  138. package/lib/share/ui/ShareDialogContent.js +36 -30
  139. package/lib/share/ui/ShareDialogContent.js.map +1 -1
  140. package/lib/share/ui/ShareLinkBar.d.ts +21 -0
  141. package/lib/share/ui/ShareLinkBar.d.ts.map +1 -0
  142. package/lib/share/ui/ShareLinkBar.js +32 -0
  143. package/lib/share/ui/ShareLinkBar.js.map +1 -0
  144. package/lib/share/ui/SharePanes.d.ts +17 -0
  145. package/lib/share/ui/SharePanes.d.ts.map +1 -0
  146. package/lib/share/ui/SharePanes.js +27 -0
  147. package/lib/share/ui/SharePanes.js.map +1 -0
  148. package/lib/share/ui/SharePartOptions.d.ts +9 -3
  149. package/lib/share/ui/SharePartOptions.d.ts.map +1 -1
  150. package/lib/share/ui/SharePartOptions.js +12 -16
  151. package/lib/share/ui/SharePartOptions.js.map +1 -1
  152. package/lib/share/ui/SharePartRow.d.ts +37 -0
  153. package/lib/share/ui/SharePartRow.d.ts.map +1 -0
  154. package/lib/share/ui/SharePartRow.js +25 -0
  155. package/lib/share/ui/SharePartRow.js.map +1 -0
  156. package/lib/share/ui/SharePresetTabs.d.ts +10 -7
  157. package/lib/share/ui/SharePresetTabs.d.ts.map +1 -1
  158. package/lib/share/ui/SharePresetTabs.js +17 -17
  159. package/lib/share/ui/SharePresetTabs.js.map +1 -1
  160. package/lib/share/ui/SharePreview.d.ts +32 -0
  161. package/lib/share/ui/SharePreview.d.ts.map +1 -0
  162. package/lib/share/ui/SharePreview.js +129 -0
  163. package/lib/share/ui/SharePreview.js.map +1 -0
  164. package/lib/share/ui/SharePreviewDevices.d.ts +20 -0
  165. package/lib/share/ui/SharePreviewDevices.d.ts.map +1 -0
  166. package/lib/share/ui/SharePreviewDevices.js +40 -0
  167. package/lib/share/ui/SharePreviewDevices.js.map +1 -0
  168. package/lib/share/ui/index.d.ts +1 -0
  169. package/lib/share/ui/index.d.ts.map +1 -1
  170. package/lib/share/ui/index.js.map +1 -1
  171. package/lib/share/ui/shareMarking.d.ts +14 -0
  172. package/lib/share/ui/shareMarking.d.ts.map +1 -0
  173. package/lib/share/ui/shareMarking.js +17 -0
  174. package/lib/share/ui/shareMarking.js.map +1 -0
  175. package/lib/share/ui/useShareRegionReports.d.ts +12 -0
  176. package/lib/share/ui/useShareRegionReports.d.ts.map +1 -0
  177. package/lib/share/ui/useShareRegionReports.js +102 -0
  178. package/lib/share/ui/useShareRegionReports.js.map +1 -0
  179. package/lib/shared/ui/ActionTooltip.d.ts +42 -0
  180. package/lib/shared/ui/ActionTooltip.d.ts.map +1 -0
  181. package/lib/shared/ui/ActionTooltip.js +21 -0
  182. package/lib/shared/ui/ActionTooltip.js.map +1 -0
  183. package/lib/shared/ui/index.d.ts +4 -0
  184. package/lib/shared/ui/index.d.ts.map +1 -1
  185. package/lib/shared/ui/index.js +2 -0
  186. package/lib/shared/ui/index.js.map +1 -1
  187. package/lib/shared/ui/useActionTooltip.d.ts +48 -0
  188. package/lib/shared/ui/useActionTooltip.d.ts.map +1 -0
  189. package/lib/shared/ui/useActionTooltip.js +78 -0
  190. package/lib/shared/ui/useActionTooltip.js.map +1 -0
  191. package/lib/structure/core/index.d.ts +2 -0
  192. package/lib/structure/core/index.d.ts.map +1 -1
  193. package/lib/structure/core/index.js +1 -0
  194. package/lib/structure/core/index.js.map +1 -1
  195. package/lib/structure/core/sourceMolecule.d.ts +18 -0
  196. package/lib/structure/core/sourceMolecule.d.ts.map +1 -0
  197. package/lib/structure/core/sourceMolecule.js +26 -0
  198. package/lib/structure/core/sourceMolecule.js.map +1 -0
  199. package/lib/structure/core/structureNotations.d.ts +57 -0
  200. package/lib/structure/core/structureNotations.d.ts.map +1 -0
  201. package/lib/structure/core/structureNotations.js +74 -0
  202. package/lib/structure/core/structureNotations.js.map +1 -0
  203. package/lib/structure/core/structurePicture.d.ts +49 -0
  204. package/lib/structure/core/structurePicture.d.ts.map +1 -0
  205. package/lib/structure/core/structurePicture.js +83 -0
  206. package/lib/structure/core/structurePicture.js.map +1 -0
  207. package/lib/structure/ui/EditorExportButton.d.ts +23 -0
  208. package/lib/structure/ui/EditorExportButton.d.ts.map +1 -0
  209. package/lib/structure/ui/EditorExportButton.js +23 -0
  210. package/lib/structure/ui/EditorExportButton.js.map +1 -0
  211. package/lib/structure/ui/EditorHelpButton.d.ts +5 -2
  212. package/lib/structure/ui/EditorHelpButton.d.ts.map +1 -1
  213. package/lib/structure/ui/EditorHelpButton.js +8 -9
  214. package/lib/structure/ui/EditorHelpButton.js.map +1 -1
  215. package/lib/structure/ui/StructureEditor.d.ts +8 -0
  216. package/lib/structure/ui/StructureEditor.d.ts.map +1 -1
  217. package/lib/structure/ui/StructureEditor.js +26 -3
  218. package/lib/structure/ui/StructureEditor.js.map +1 -1
  219. package/lib/structure/ui/StructureExportDialog.d.ts +34 -0
  220. package/lib/structure/ui/StructureExportDialog.d.ts.map +1 -0
  221. package/lib/structure/ui/StructureExportDialog.js +43 -0
  222. package/lib/structure/ui/StructureExportDialog.js.map +1 -0
  223. package/lib/structure/ui/StructureExportPanel.d.ts +37 -0
  224. package/lib/structure/ui/StructureExportPanel.d.ts.map +1 -0
  225. package/lib/structure/ui/StructureExportPanel.js +75 -0
  226. package/lib/structure/ui/StructureExportPanel.js.map +1 -0
  227. package/lib/structure/ui/StructureNotationRow.d.ts +22 -0
  228. package/lib/structure/ui/StructureNotationRow.d.ts.map +1 -0
  229. package/lib/structure/ui/StructureNotationRow.js +34 -0
  230. package/lib/structure/ui/StructureNotationRow.js.map +1 -0
  231. package/lib/structure/ui/StructurePicturePane.d.ts +23 -0
  232. package/lib/structure/ui/StructurePicturePane.d.ts.map +1 -0
  233. package/lib/structure/ui/StructurePicturePane.js +113 -0
  234. package/lib/structure/ui/StructurePicturePane.js.map +1 -0
  235. package/lib/structure/ui/StructureSvg.d.ts.map +1 -1
  236. package/lib/structure/ui/StructureSvg.js +2 -18
  237. package/lib/structure/ui/StructureSvg.js.map +1 -1
  238. package/lib/structure/ui/ToolbarTooltip.d.ts.map +1 -1
  239. package/lib/structure/ui/ToolbarTooltip.js +1 -2
  240. package/lib/structure/ui/ToolbarTooltip.js.map +1 -1
  241. package/lib/structure/ui/index.d.ts +7 -3
  242. package/lib/structure/ui/index.d.ts.map +1 -1
  243. package/lib/structure/ui/index.js +5 -3
  244. package/lib/structure/ui/index.js.map +1 -1
  245. package/lib/structure/ui/structureNotationText.d.ts +23 -0
  246. package/lib/structure/ui/structureNotationText.d.ts.map +1 -0
  247. package/lib/structure/ui/structureNotationText.js +42 -0
  248. package/lib/structure/ui/structureNotationText.js.map +1 -0
  249. package/lib/structure/ui/useEditorStructure.d.ts +37 -0
  250. package/lib/structure/ui/useEditorStructure.d.ts.map +1 -0
  251. package/lib/structure/ui/useEditorStructure.js +70 -0
  252. package/lib/structure/ui/useEditorStructure.js.map +1 -0
  253. package/package.json +1 -1
  254. package/src/chart/ui/Axes.tsx +53 -5
  255. package/src/chrome/ui/NavMenuButton.tsx +61 -18
  256. package/src/chrome/ui/SiteHeader.tsx +37 -1
  257. package/src/chrome/ui/navItem.ts +7 -0
  258. package/src/citation/ui/entries.tsx +10 -2
  259. package/src/clipboard/ui/ClickToCopy.tsx +47 -13
  260. package/src/clipboard/ui/CopyButton.tsx +4 -2
  261. package/src/clipboard/ui/CopyableValue.tsx +88 -13
  262. package/src/clipboard/ui/index.ts +5 -1
  263. package/src/confirm/ui/ConfirmButton.tsx +2 -0
  264. package/src/delimited/ui/TableDataButton.tsx +2 -1
  265. package/src/download/core/figureSvg.ts +42 -10
  266. package/src/help/ui/HelpToolbarButton.tsx +2 -1
  267. package/src/locales/de.json +39 -4
  268. package/src/locales/en.json +39 -4
  269. package/src/locales/es.json +39 -4
  270. package/src/locales/fr.json +39 -4
  271. package/src/locales/it.json +39 -4
  272. package/src/number/ui/NumberInput.tsx +1 -1
  273. package/src/overlay/ui/OverlayIconButton.tsx +1 -0
  274. package/src/panel/ui/ClearButton.tsx +1 -0
  275. package/src/pedagogy/core/inlineMarks.ts +15 -7
  276. package/src/pedagogy/ui/GlossaryDefinition.tsx +8 -18
  277. package/src/pedagogy/ui/GlossaryIndex.tsx +0 -1
  278. package/src/pedagogy/ui/GlossaryProvider.tsx +10 -2
  279. package/src/pedagogy/ui/GlossaryText.tsx +10 -2
  280. package/src/pedagogy/ui/InlineText.tsx +21 -4
  281. package/src/pedagogy/ui/ReferenceSectionBlock.tsx +8 -2
  282. package/src/pedagogy/ui/SyntaxTooltip.tsx +11 -9
  283. package/src/pedagogy/ui/TutorialStepStrip.tsx +50 -18
  284. package/src/pedagogy/ui/glossaryContext.ts +7 -0
  285. package/src/pedagogy/ui/index.ts +1 -1
  286. package/src/pedagogy/ui/inlineSegments.tsx +7 -0
  287. package/src/pedagogy/ui/pedagogyStyle.ts +12 -24
  288. package/src/seo/core/plainDescription.ts +4 -2
  289. package/src/share/core/devices.ts +80 -0
  290. package/src/share/core/index.ts +14 -0
  291. package/src/share/core/regions.ts +87 -0
  292. package/src/share/ui/HiddenPartsProvider.tsx +6 -1
  293. package/src/share/ui/PagePart.tsx +12 -1
  294. package/src/share/ui/ShareButton.tsx +3 -6
  295. package/src/share/ui/ShareConfigOptions.tsx +25 -16
  296. package/src/share/ui/ShareDialog.tsx +17 -3
  297. package/src/share/ui/ShareDialogContent.tsx +80 -77
  298. package/src/share/ui/ShareLinkBar.tsx +70 -0
  299. package/src/share/ui/SharePanes.tsx +62 -0
  300. package/src/share/ui/SharePartOptions.tsx +25 -26
  301. package/src/share/ui/SharePartRow.tsx +78 -0
  302. package/src/share/ui/SharePresetTabs.tsx +36 -43
  303. package/src/share/ui/SharePreview.tsx +235 -0
  304. package/src/share/ui/SharePreviewDevices.tsx +70 -0
  305. package/src/share/ui/index.ts +1 -0
  306. package/src/share/ui/shareMarking.ts +18 -0
  307. package/src/share/ui/useShareRegionReports.ts +108 -0
  308. package/src/shared/ui/ActionTooltip.tsx +70 -0
  309. package/src/shared/ui/index.ts +7 -0
  310. package/src/shared/ui/useActionTooltip.ts +103 -0
  311. package/src/structure/core/index.ts +5 -0
  312. package/src/structure/core/sourceMolecule.ts +26 -0
  313. package/src/structure/core/structureNotations.ts +125 -0
  314. package/src/structure/core/structurePicture.ts +119 -0
  315. package/src/structure/ui/EditorExportButton.tsx +62 -0
  316. package/src/structure/ui/EditorHelpButton.tsx +26 -30
  317. package/src/structure/ui/StructureEditor.tsx +51 -5
  318. package/src/structure/ui/StructureExportDialog.tsx +90 -0
  319. package/src/structure/ui/StructureExportPanel.tsx +156 -0
  320. package/src/structure/ui/StructureNotationRow.tsx +74 -0
  321. package/src/structure/ui/StructurePicturePane.tsx +208 -0
  322. package/src/structure/ui/StructureSvg.tsx +3 -12
  323. package/src/structure/ui/ToolbarTooltip.tsx +1 -1
  324. package/src/structure/ui/index.ts +7 -3
  325. package/src/structure/ui/structureNotationText.ts +56 -0
  326. package/src/structure/ui/useEditorStructure.ts +119 -0
  327. package/styles/chrome.css +414 -14
@@ -1,15 +1,9 @@
1
1
  import { Tab, Tabs } from '@blueprintjs/core';
2
- import type { CSSProperties, ReactElement, ReactNode } from 'react';
2
+ import type { ReactElement } from 'react';
3
3
 
4
4
  import { useChromeT } from '../../i18n/ui/useT.ts';
5
5
  import type { ShareParamCodecs, SharePreset } from '../core/index.ts';
6
6
 
7
- const SECTION_STYLE: CSSProperties = { marginBottom: 18 };
8
- const DESCRIPTION_STYLE: CSSProperties = {
9
- margin: 0,
10
- color: 'var(--text-muted, #5b6875)',
11
- };
12
-
13
7
  const CUSTOM_TAB = 'custom';
14
8
  const PRESET_TAB_PREFIX = 'preset-';
15
9
 
@@ -22,51 +16,50 @@ export interface SharePresetTabsProps<
22
16
  selected: string | null;
23
17
  /** Called with the key of the preset picked, `null` for the custom tab. */
24
18
  onSelect: (key: string | null) => void;
25
- /** What the custom tab holds: the boxes of the draft. */
26
- custom: ReactNode;
27
19
  }
28
20
 
29
21
  /**
30
- * One tab per preset, then a last Custom tab holding the boxes, which start
31
- * from whatever the preset before it set.
32
- * @param props - The presets, the open one, and the custom panel.
33
- * @returns The tab strip and the open panel.
22
+ * One tab per ready-made link, then a last Custom tab for building any other
23
+ * one.
24
+ *
25
+ * The strip runs the width of the dialog rather than sitting in the column of
26
+ * options, because the choice governs the preview as much as the switches: in
27
+ * a third of the width, four tabs wrap onto a second row and the last of them
28
+ * — the one holding every switch — reads as an afterthought.
29
+ * @param props - The presets, and which one is open.
30
+ * @returns The tab strip.
34
31
  */
35
32
  export function SharePresetTabs<
36
33
  Codecs extends ShareParamCodecs = Record<string, never>,
37
34
  >(props: SharePresetTabsProps<Codecs>): ReactElement {
38
- const { presets, selected, onSelect, custom } = props;
35
+ const { presets, selected, onSelect } = props;
39
36
  const t = useChromeT();
40
37
 
41
38
  return (
42
- <section className="share-section" style={SECTION_STYLE}>
43
- <Tabs
44
- id="share-presets"
45
- className="share-presets"
46
- animate={false}
47
- renderActiveTabPanelOnly
48
- selectedTabId={
49
- selected === null ? CUSTOM_TAB : `${PRESET_TAB_PREFIX}${selected}`
50
- }
51
- onChange={(tabId) => {
52
- const id = String(tabId);
53
- onSelect(
54
- id.startsWith(PRESET_TAB_PREFIX)
55
- ? id.slice(PRESET_TAB_PREFIX.length)
56
- : null,
57
- );
58
- }}
59
- >
60
- {presets.map((preset) => (
61
- <Tab
62
- key={preset.key}
63
- id={`${PRESET_TAB_PREFIX}${preset.key}`}
64
- title={preset.label}
65
- panel={<p style={DESCRIPTION_STYLE}>{preset.description}</p>}
66
- />
67
- ))}
68
- <Tab id={CUSTOM_TAB} title={t('share.custom')} panel={<>{custom}</>} />
69
- </Tabs>
70
- </section>
39
+ <Tabs
40
+ id="share-presets"
41
+ className="share-presets"
42
+ animate={false}
43
+ selectedTabId={
44
+ selected === null ? CUSTOM_TAB : `${PRESET_TAB_PREFIX}${selected}`
45
+ }
46
+ onChange={(tabId) => {
47
+ const id = String(tabId);
48
+ onSelect(
49
+ id.startsWith(PRESET_TAB_PREFIX)
50
+ ? id.slice(PRESET_TAB_PREFIX.length)
51
+ : null,
52
+ );
53
+ }}
54
+ >
55
+ {presets.map((preset) => (
56
+ <Tab
57
+ key={preset.key}
58
+ id={`${PRESET_TAB_PREFIX}${preset.key}`}
59
+ title={preset.label}
60
+ />
61
+ ))}
62
+ <Tab id={CUSTOM_TAB} title={t('share.custom')} />
63
+ </Tabs>
71
64
  );
72
65
  }
@@ -0,0 +1,235 @@
1
+ import { H6, Icon } from '@blueprintjs/core';
2
+ import type { CSSProperties, ReactElement } from 'react';
3
+ import { useEffect, useRef, useState } from 'react';
4
+
5
+ import { formatInteger } from '../../format/core/index.ts';
6
+ import type { HelpContent } from '../../help/ui/index.ts';
7
+ import { HelpIcon } from '../../help/ui/index.ts';
8
+ import { useContainerSize } from '../../hooks/ui/useContainerSize.ts';
9
+ import { useDebouncedValue } from '../../hooks/ui/useDebouncedValue.ts';
10
+ import { useChromeT } from '../../i18n/ui/useT.ts';
11
+ import type { SharePreviewDeviceKey, ShareRegion } from '../core/index.ts';
12
+ import {
13
+ SHARE_REGIONS_REQUEST,
14
+ readShareRegions,
15
+ sharePreviewAddress,
16
+ sharePreviewScale,
17
+ sharePreviewSize,
18
+ } from '../core/index.ts';
19
+
20
+ import { SharePreviewDevices } from './SharePreviewDevices.tsx';
21
+
22
+ /** Room the window's own bar takes, which the page is not scaled into. */
23
+ const CHROME_HEIGHT = 26;
24
+
25
+ /** How often the framed page is asked again while it has answered nothing. */
26
+ const ASK_INTERVAL = 300;
27
+
28
+ /** How many times, so a page with nothing to report is left alone. */
29
+ const ASK_LIMIT = 10;
30
+
31
+ export interface SharePreviewProps {
32
+ /** The address the frame loads — the link as it stands. */
33
+ url: string;
34
+ /** What a screen reader announces the frame as. */
35
+ title: string;
36
+ /**
37
+ * The part the reader is pointing at in the list, drawn on the page so the
38
+ * name in the list and the thing on the page are the same thing.
39
+ * @default null — nothing is pointed at
40
+ */
41
+ highlight?: { part: string; label: string } | null;
42
+ }
43
+
44
+ /**
45
+ * The page as the link hands it out, live, beside the options that write it.
46
+ *
47
+ * It is drawn as a window of its own — named above it, with the address it
48
+ * loads on its own bar — because a page scaled into a pane otherwise reads as
49
+ * the site itself, and a reader who takes the preview for the tool ticks a box
50
+ * in it and believes the link now carries it.
51
+ *
52
+ * The frame is laid out at the screen the reader picks — their own window, a
53
+ * phone, a laptop or a large display — and scaled into whatever room the pane
54
+ * has, so a course tile can be checked at the size a student will meet it.
55
+ * @param props - The address and the name of the frame.
56
+ * @returns The preview pane.
57
+ */
58
+ export function SharePreview(props: SharePreviewProps): ReactElement {
59
+ const { url, title, highlight = null } = props;
60
+ const t = useChromeT();
61
+ const stage = useRef<HTMLDivElement>(null);
62
+ const frame = useRef<HTMLIFrameElement>(null);
63
+ const pane = useContainerSize(stage);
64
+ const [device, setDevice] = useState<SharePreviewDeviceKey>('window');
65
+ const [regions, setRegions] = useState<readonly ShareRegion[]>(NO_REGIONS);
66
+ const mark = useRef<HTMLDivElement>(null);
67
+ // Every box ticked reloads the frame; a number typed into a tool's own field
68
+ // would otherwise reload it once per keystroke.
69
+ const src = useDebouncedValue(url, 400);
70
+
71
+ const windowSize = {
72
+ width: globalThis.innerWidth,
73
+ height: globalThis.innerHeight,
74
+ };
75
+ const page = sharePreviewSize(device, windowSize);
76
+ const scale = sharePreviewScale(
77
+ { width: pane.width, height: pane.height - CHROME_HEIGHT },
78
+ page,
79
+ );
80
+ const region =
81
+ highlight === null ? undefined : regionOf(regions, highlight.part);
82
+ const help: HelpContent = {
83
+ title: t('share.preview'),
84
+ body: [t('share.previewWhat'), t('share.previewLive')],
85
+ };
86
+
87
+ useEffect(() => {
88
+ function onMessage(event: MessageEvent): void {
89
+ if (event.source !== frame.current?.contentWindow) return;
90
+ const reported = readShareRegions(event.data);
91
+ if (reported !== null) setRegions(reported);
92
+ }
93
+
94
+ globalThis.addEventListener('message', onMessage);
95
+ return () => {
96
+ globalThis.removeEventListener('message', onMessage);
97
+ };
98
+ }, []);
99
+
100
+ // A region further down the page than the stage shows is brought into view,
101
+ // or pointing at a row would mark something the reader cannot see.
102
+ useEffect(() => {
103
+ mark.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
104
+ }, [highlight?.part]);
105
+
106
+ // The frame's own load says the document is there, never that the page it
107
+ // builds is listening: a React tree mounts in a task of its own, after it.
108
+ // So the question is repeated until it is answered — there is no event on
109
+ // this side to wait for, and a page that answers nothing is a page with no
110
+ // parts to point at, which costs ten messages and stops.
111
+ useEffect(() => {
112
+ if (regions.length > 0) return undefined;
113
+ let asked = 0;
114
+ const timer = setInterval(() => {
115
+ asked += 1;
116
+ if (asked > ASK_LIMIT) {
117
+ clearInterval(timer);
118
+ return;
119
+ }
120
+ frame.current?.contentWindow?.postMessage(
121
+ { type: SHARE_REGIONS_REQUEST },
122
+ '*',
123
+ );
124
+ }, ASK_INTERVAL);
125
+
126
+ return () => {
127
+ clearInterval(timer);
128
+ };
129
+ }, [regions, src]);
130
+
131
+ return (
132
+ <section className="share-preview" aria-label={t('share.preview')}>
133
+ <header className="share-preview__header">
134
+ <H6 className="share-preview__label">
135
+ <Icon icon="eye-open" size={13} />
136
+ {t('share.preview')}
137
+ <HelpIcon content={help} />
138
+ </H6>
139
+ <SharePreviewDevices
140
+ value={device}
141
+ onChange={setDevice}
142
+ windowSize={windowSize}
143
+ />
144
+ </header>
145
+ <div className="share-preview__stage" ref={stage}>
146
+ <div
147
+ className="share-preview__device"
148
+ style={{ width: page.width * scale }}
149
+ >
150
+ <div className="share-preview__chrome">
151
+ <Icon icon="eye-open" size={11} />
152
+ <span className="share-preview__address">
153
+ {sharePreviewAddress(url)}
154
+ </span>
155
+ <span className="share-preview__size">
156
+ {sizeNote(page.width, page.height, scale)}
157
+ </span>
158
+ </div>
159
+ <div
160
+ className="share-preview__page"
161
+ style={{ height: page.height * scale }}
162
+ >
163
+ <iframe
164
+ ref={frame}
165
+ className="share-preview__frame"
166
+ src={src}
167
+ title={title}
168
+ style={frameStyle(page.width, page.height, scale)}
169
+ onLoad={() => {
170
+ // What the page before it reported is where its own parts
171
+ // were, so it is dropped rather than drawn over this one.
172
+ setRegions(NO_REGIONS);
173
+ frame.current?.contentWindow?.postMessage(
174
+ { type: SHARE_REGIONS_REQUEST },
175
+ '*',
176
+ );
177
+ }}
178
+ />
179
+ {region === undefined || highlight === null ? null : (
180
+ <div
181
+ ref={mark}
182
+ className="share-preview__region"
183
+ style={regionStyle(region, scale)}
184
+ >
185
+ <span className="share-preview__region-name">
186
+ {highlight.label}
187
+ </span>
188
+ </div>
189
+ )}
190
+ </div>
191
+ </div>
192
+ </div>
193
+ </section>
194
+ );
195
+ }
196
+
197
+ const NO_REGIONS: readonly ShareRegion[] = [];
198
+
199
+ function regionOf(
200
+ regions: readonly ShareRegion[],
201
+ part: string,
202
+ ): ShareRegion | undefined {
203
+ for (const region of regions) {
204
+ if (region.part === part) return region;
205
+ }
206
+ return undefined;
207
+ }
208
+
209
+ function regionStyle(region: ShareRegion, scale: number): CSSProperties {
210
+ return {
211
+ left: region.x * scale,
212
+ top: region.y * scale,
213
+ width: region.width * scale,
214
+ height: region.height * scale,
215
+ };
216
+ }
217
+
218
+ function frameStyle(
219
+ width: number,
220
+ height: number,
221
+ scale: number,
222
+ ): CSSProperties {
223
+ return {
224
+ width,
225
+ height,
226
+ border: 0,
227
+ transform: `scale(${scale})`,
228
+ transformOrigin: 'top left',
229
+ };
230
+ }
231
+
232
+ function sizeNote(width: number, height: number, scale: number): string {
233
+ const size = `${formatInteger(width)} × ${formatInteger(height)}`;
234
+ return scale === 1 ? size : `${size} · ${Math.round(scale * 100)}%`;
235
+ }
@@ -0,0 +1,70 @@
1
+ import type { IconName } from '@blueprintjs/core';
2
+ import { Button, ButtonGroup } from '@blueprintjs/core';
3
+ import type { ReactElement } from 'react';
4
+
5
+ import { useChromeT } from '../../i18n/ui/useT.ts';
6
+ import type { SharePreviewDeviceKey, SharePreviewSize } from '../core/index.ts';
7
+ import { SHARE_PREVIEW_DEVICES, sharePreviewSize } from '../core/index.ts';
8
+
9
+ /**
10
+ * The glyph each screen is picked by: a window of one's own, a phone, a
11
+ * monitor, a projected display. The row reads as four screen sizes at a
12
+ * glance, which four words never did.
13
+ */
14
+ const DEVICE_ICONS: Record<SharePreviewDeviceKey, IconName> = {
15
+ window: 'application',
16
+ mobile: 'mobile-phone',
17
+ laptop: 'desktop',
18
+ hd: 'presentation',
19
+ };
20
+
21
+ export interface SharePreviewDevicesProps {
22
+ /** The screen the preview is laid out at. */
23
+ value: SharePreviewDeviceKey;
24
+ /** Called with the screen picked. */
25
+ onChange: (value: SharePreviewDeviceKey) => void;
26
+ /** The reader's own window, which the first screen is measured from. */
27
+ windowSize: Partial<SharePreviewSize>;
28
+ }
29
+
30
+ /**
31
+ * The screens a link can be looked at on, as one row of glyphs.
32
+ *
33
+ * Each carries its name and its size, so a pointer resting on one says what it
34
+ * lays the page out at rather than leaving the reader to guess the icon.
35
+ * @param props - See {@link SharePreviewDevicesProps}.
36
+ * @returns The row of screen buttons.
37
+ */
38
+ export function SharePreviewDevices(
39
+ props: SharePreviewDevicesProps,
40
+ ): ReactElement {
41
+ const { value, onChange, windowSize } = props;
42
+ const t = useChromeT();
43
+
44
+ return (
45
+ <ButtonGroup className="share-preview__devices" variant="minimal">
46
+ {SHARE_PREVIEW_DEVICES.map((key) => {
47
+ const size = sharePreviewSize(key, windowSize);
48
+ const name = t(`share.device.${key}`);
49
+ const label = t('share.deviceSize', {
50
+ name,
51
+ width: size.width,
52
+ height: size.height,
53
+ });
54
+
55
+ return (
56
+ <Button
57
+ key={key}
58
+ icon={DEVICE_ICONS[key]}
59
+ active={key === value}
60
+ title={label}
61
+ aria-label={label}
62
+ onClick={() => {
63
+ onChange(key);
64
+ }}
65
+ />
66
+ );
67
+ })}
68
+ </ButtonGroup>
69
+ );
70
+ }
@@ -6,4 +6,5 @@ export { PagePart } from './PagePart.tsx';
6
6
  export type { ShareButtonProps, ShareButtonVariant } from './ShareButton.tsx';
7
7
  export { ShareButton } from './ShareButton.tsx';
8
8
  export type { ShareDialogProps } from './ShareDialog.tsx';
9
+ export type { SharePartDescriptions } from './SharePartRow.tsx';
9
10
  export { ShareDialog } from './ShareDialog.tsx';
@@ -0,0 +1,18 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ /**
4
+ * Whether the page is being looked at through a share dialog's preview, and so
5
+ * marks each of its parts in the DOM for the dialog to find.
6
+ *
7
+ * A page nobody is framing marks nothing: the marker is a wrapper, and a
8
+ * wrapper changes what a `>` selector in a site's own stylesheet matches.
9
+ */
10
+ export const ShareMarkingContext = createContext(false);
11
+
12
+ /**
13
+ * Whether this page marks its parts for a share dialog looking at it.
14
+ * @returns Whether the markers are rendered.
15
+ */
16
+ export function useShareMarking(): boolean {
17
+ return useContext(ShareMarkingContext);
18
+ }
@@ -0,0 +1,108 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import type { ShareRegion } from '../core/index.ts';
4
+ import { SHARE_REGIONS_MESSAGE, isShareRegionsRequest } from '../core/index.ts';
5
+
6
+ /**
7
+ * Report where each part of this page sits, while a share dialog is looking at
8
+ * it through its preview.
9
+ *
10
+ * Nothing happens on a page nobody frames, and nothing happens in a frame
11
+ * whose host never asks — a page embedded in a course is framed too, and owes
12
+ * its host nothing.
13
+ * @param hidden - The parts the link switches off, so the boxes are measured again when they change.
14
+ * @returns Whether the page should mark its parts for the dialog to find.
15
+ */
16
+ export function useShareRegionReports(hidden: ReadonlySet<string>): boolean {
17
+ const [marking, setMarking] = useState(false);
18
+
19
+ useEffect(() => {
20
+ const host = globalThis.parent;
21
+ if (host === globalThis.self) return undefined;
22
+
23
+ function onMessage(event: MessageEvent): void {
24
+ if (isShareRegionsRequest(event.data)) setMarking(true);
25
+ }
26
+
27
+ globalThis.addEventListener('message', onMessage);
28
+ return () => {
29
+ globalThis.removeEventListener('message', onMessage);
30
+ };
31
+ }, []);
32
+
33
+ useEffect(() => {
34
+ if (!marking) return undefined;
35
+ const host = globalThis.parent;
36
+ let scheduled = 0;
37
+ let reported = '';
38
+
39
+ function report(): void {
40
+ scheduled = 0;
41
+ const regions = measureRegions();
42
+ // A page that is drawing — a structure, a result, a chart — mutates many
43
+ // times a second while its boxes stay where they are, and every message
44
+ // sent re-renders the dialog on the other side. Only a move is news.
45
+ const signature = JSON.stringify(regions);
46
+ if (signature === reported) return;
47
+ reported = signature;
48
+ host.postMessage({ type: SHARE_REGIONS_MESSAGE, regions }, '*');
49
+ }
50
+
51
+ // A search that returns, a structure that draws and a font that lands all
52
+ // move the boxes within a frame of each other, so the page is measured
53
+ // once after them rather than once per notification.
54
+ function schedule(): void {
55
+ if (scheduled === 0) scheduled = requestAnimationFrame(report);
56
+ }
57
+
58
+ // A chart, a web font or a structure drawn after mount all move the boxes,
59
+ // so the page is measured again whenever anything in it changes rather
60
+ // than once, on a deadline it would lose. Watching its size is not enough:
61
+ // a page laid out to the height of the viewport keeps that height while a
62
+ // result appears inside a column of it, and the part nobody measured is
63
+ // the one the reader is pointing at.
64
+ const resizes = new ResizeObserver(schedule);
65
+ resizes.observe(document.documentElement);
66
+ resizes.observe(document.body);
67
+ const mutations = new MutationObserver(schedule);
68
+ mutations.observe(document.body, { childList: true, subtree: true });
69
+ globalThis.addEventListener('resize', schedule);
70
+ report();
71
+
72
+ return () => {
73
+ if (scheduled !== 0) cancelAnimationFrame(scheduled);
74
+ resizes.disconnect();
75
+ mutations.disconnect();
76
+ globalThis.removeEventListener('resize', schedule);
77
+ };
78
+ }, [marking, hidden]);
79
+
80
+ return marking;
81
+ }
82
+
83
+ function measureRegions(): ShareRegion[] {
84
+ const markers = document.querySelectorAll<HTMLElement>('[data-share-part]');
85
+ const range = document.createRange();
86
+ const regions: ShareRegion[] = [];
87
+
88
+ for (const marker of markers) {
89
+ const { sharePart: part } = marker.dataset;
90
+ if (part === undefined) continue;
91
+ // The marker lays out as `display: contents`, so it has no box of its own.
92
+ // A range over what it holds has the box its contents occupy.
93
+ range.selectNodeContents(marker);
94
+ const box = range.getBoundingClientRect();
95
+ if (box.width === 0 || box.height === 0) continue;
96
+ // Rounded, so a layout that settles half a pixel to the left is not
97
+ // reported as a part that moved.
98
+ regions.push({
99
+ part,
100
+ x: Math.round(box.x + globalThis.scrollX),
101
+ y: Math.round(box.y + globalThis.scrollY),
102
+ width: Math.round(box.width),
103
+ height: Math.round(box.height),
104
+ });
105
+ }
106
+
107
+ return regions;
108
+ }
@@ -0,0 +1,70 @@
1
+ import type { Placement } from '@blueprintjs/core';
2
+ import { Tooltip } from '@blueprintjs/core';
3
+ import type { CSSProperties, ReactElement } from 'react';
4
+
5
+ import { useActionTooltip } from './useActionTooltip.ts';
6
+
7
+ /** What {@link ActionTooltip} explains, and what it steps aside for. */
8
+ export interface ActionTooltipProps {
9
+ /** What the control is for, read on hover. */
10
+ content: string | ReactElement;
11
+ /** The control. */
12
+ children: ReactElement;
13
+ /**
14
+ * Whether what the control opens is on screen. While it is, the tooltip
15
+ * stays shut.
16
+ * @default false
17
+ */
18
+ opened?: boolean;
19
+ /**
20
+ * Which side of the control the tooltip is drawn on.
21
+ * @default 'bottom'
22
+ */
23
+ placement?: Placement;
24
+ /**
25
+ * Class the popover carries, for a card that is not the chrome's own.
26
+ * @default 'help-tooltip'
27
+ */
28
+ popoverClassName?: string;
29
+ /**
30
+ * How long the pointer rests on the control before the card is drawn, in
31
+ * milliseconds.
32
+ * @default 250
33
+ */
34
+ delay?: number;
35
+ }
36
+
37
+ /**
38
+ * A tooltip on a control that opens something — a dialog, a popover, a menu.
39
+ *
40
+ * It is the wrapper form of {@link useActionTooltip}, which is where the whole
41
+ * argument is written; a tooltip whose target cannot take a wrapping element —
42
+ * a menu item, a table cell — uses the hook directly.
43
+ * @param props - See {@link ActionTooltipProps}.
44
+ * @returns The control, with its tooltip.
45
+ */
46
+ export function ActionTooltip(props: ActionTooltipProps): ReactElement {
47
+ const { content, children, opened = false } = props;
48
+ const { placement = 'bottom', popoverClassName = 'help-tooltip' } = props;
49
+ const { delay } = props;
50
+ const { isOpen, target } = useActionTooltip(opened, delay);
51
+
52
+ return (
53
+ <Tooltip
54
+ content={content}
55
+ placement={placement}
56
+ popoverClassName={popoverClassName}
57
+ isOpen={isOpen}
58
+ >
59
+ {/* The pointer is read here rather than on the control, so the control
60
+ keeps whatever handlers it was given. A box of its own, never
61
+ `display: contents`, which measures as an empty rectangle and leaves
62
+ the card positioned in the corner of the window. */}
63
+ <span style={WRAP_STYLE} {...target}>
64
+ {children}
65
+ </span>
66
+ </Tooltip>
67
+ );
68
+ }
69
+
70
+ const WRAP_STYLE = { display: 'inline-flex' } as const satisfies CSSProperties;
@@ -1,2 +1,9 @@
1
+ export type { ActionTooltipProps } from './ActionTooltip.tsx';
2
+ export { ActionTooltip } from './ActionTooltip.tsx';
1
3
  export type { HeaderButtonProps, MenuButtonProps } from './MenuButton.tsx';
2
4
  export { MenuButton } from './MenuButton.tsx';
5
+ export type {
6
+ ActionTooltipState,
7
+ ActionTooltipTarget,
8
+ } from './useActionTooltip.ts';
9
+ export { ACTION_TOOLTIP_DELAY, useActionTooltip } from './useActionTooltip.ts';