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.
- package/lib/chart/ui/Axes.d.ts +19 -0
- package/lib/chart/ui/Axes.d.ts.map +1 -1
- package/lib/chart/ui/Axes.js +25 -5
- package/lib/chart/ui/Axes.js.map +1 -1
- package/lib/chrome/ui/NavMenuButton.d.ts.map +1 -1
- package/lib/chrome/ui/NavMenuButton.js +41 -11
- package/lib/chrome/ui/NavMenuButton.js.map +1 -1
- package/lib/chrome/ui/SiteHeader.js +24 -1
- package/lib/chrome/ui/SiteHeader.js.map +1 -1
- package/lib/chrome/ui/navItem.d.ts +7 -0
- package/lib/chrome/ui/navItem.d.ts.map +1 -1
- package/lib/chrome/ui/navItem.js.map +1 -1
- package/lib/citation/ui/entries.d.ts.map +1 -1
- package/lib/citation/ui/entries.js +6 -1
- package/lib/citation/ui/entries.js.map +1 -1
- package/lib/clipboard/ui/ClickToCopy.d.ts +11 -0
- package/lib/clipboard/ui/ClickToCopy.d.ts.map +1 -1
- package/lib/clipboard/ui/ClickToCopy.js +28 -10
- package/lib/clipboard/ui/ClickToCopy.js.map +1 -1
- package/lib/clipboard/ui/CopyButton.d.ts +2 -1
- package/lib/clipboard/ui/CopyButton.d.ts.map +1 -1
- package/lib/clipboard/ui/CopyButton.js +2 -1
- package/lib/clipboard/ui/CopyButton.js.map +1 -1
- package/lib/clipboard/ui/CopyableValue.d.ts +24 -4
- package/lib/clipboard/ui/CopyableValue.d.ts.map +1 -1
- package/lib/clipboard/ui/CopyableValue.js +35 -3
- package/lib/clipboard/ui/CopyableValue.js.map +1 -1
- package/lib/clipboard/ui/index.d.ts +1 -1
- package/lib/clipboard/ui/index.d.ts.map +1 -1
- package/lib/clipboard/ui/index.js.map +1 -1
- package/lib/confirm/ui/ConfirmButton.d.ts.map +1 -1
- package/lib/confirm/ui/ConfirmButton.js +2 -1
- package/lib/confirm/ui/ConfirmButton.js.map +1 -1
- package/lib/delimited/ui/TableDataButton.d.ts.map +1 -1
- package/lib/delimited/ui/TableDataButton.js +2 -1
- package/lib/delimited/ui/TableDataButton.js.map +1 -1
- package/lib/download/core/figureSvg.d.ts.map +1 -1
- package/lib/download/core/figureSvg.js +37 -12
- package/lib/download/core/figureSvg.js.map +1 -1
- package/lib/help/ui/HelpToolbarButton.d.ts.map +1 -1
- package/lib/help/ui/HelpToolbarButton.js +2 -1
- package/lib/help/ui/HelpToolbarButton.js.map +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
- package/lib/locales/de.json +39 -4
- package/lib/locales/en.json +39 -4
- package/lib/locales/es.json +39 -4
- package/lib/locales/fr.json +39 -4
- package/lib/locales/it.json +39 -4
- package/lib/number/ui/NumberInput.js +1 -1
- package/lib/number/ui/NumberInput.js.map +1 -1
- package/lib/overlay/ui/OverlayIconButton.d.ts.map +1 -1
- package/lib/overlay/ui/OverlayIconButton.js +1 -1
- package/lib/overlay/ui/OverlayIconButton.js.map +1 -1
- package/lib/panel/ui/ClearButton.d.ts.map +1 -1
- package/lib/panel/ui/ClearButton.js +1 -1
- package/lib/panel/ui/ClearButton.js.map +1 -1
- package/lib/pedagogy/core/inlineMarks.d.ts +9 -5
- package/lib/pedagogy/core/inlineMarks.d.ts.map +1 -1
- package/lib/pedagogy/core/inlineMarks.js +14 -6
- package/lib/pedagogy/core/inlineMarks.js.map +1 -1
- package/lib/pedagogy/ui/GlossaryDefinition.d.ts +4 -10
- package/lib/pedagogy/ui/GlossaryDefinition.d.ts.map +1 -1
- package/lib/pedagogy/ui/GlossaryDefinition.js +7 -7
- package/lib/pedagogy/ui/GlossaryDefinition.js.map +1 -1
- package/lib/pedagogy/ui/GlossaryIndex.d.ts.map +1 -1
- package/lib/pedagogy/ui/GlossaryIndex.js +1 -1
- package/lib/pedagogy/ui/GlossaryIndex.js.map +1 -1
- package/lib/pedagogy/ui/GlossaryProvider.d.ts +7 -0
- package/lib/pedagogy/ui/GlossaryProvider.d.ts.map +1 -1
- package/lib/pedagogy/ui/GlossaryProvider.js +3 -2
- package/lib/pedagogy/ui/GlossaryProvider.js.map +1 -1
- package/lib/pedagogy/ui/GlossaryText.d.ts +7 -2
- package/lib/pedagogy/ui/GlossaryText.d.ts.map +1 -1
- package/lib/pedagogy/ui/GlossaryText.js +3 -2
- package/lib/pedagogy/ui/GlossaryText.js.map +1 -1
- package/lib/pedagogy/ui/InlineText.d.ts +8 -2
- package/lib/pedagogy/ui/InlineText.d.ts.map +1 -1
- package/lib/pedagogy/ui/InlineText.js +7 -2
- package/lib/pedagogy/ui/InlineText.js.map +1 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.d.ts +4 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.d.ts.map +1 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.js +2 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.js.map +1 -1
- package/lib/pedagogy/ui/SyntaxTooltip.d.ts.map +1 -1
- package/lib/pedagogy/ui/SyntaxTooltip.js +11 -10
- package/lib/pedagogy/ui/SyntaxTooltip.js.map +1 -1
- package/lib/pedagogy/ui/TutorialStepStrip.d.ts.map +1 -1
- package/lib/pedagogy/ui/TutorialStepStrip.js +18 -4
- package/lib/pedagogy/ui/TutorialStepStrip.js.map +1 -1
- package/lib/pedagogy/ui/glossaryContext.d.ts +7 -0
- package/lib/pedagogy/ui/glossaryContext.d.ts.map +1 -1
- package/lib/pedagogy/ui/glossaryContext.js.map +1 -1
- package/lib/pedagogy/ui/index.d.ts +1 -1
- package/lib/pedagogy/ui/index.d.ts.map +1 -1
- package/lib/pedagogy/ui/inlineSegments.d.ts +5 -0
- package/lib/pedagogy/ui/inlineSegments.d.ts.map +1 -1
- package/lib/pedagogy/ui/inlineSegments.js +2 -0
- package/lib/pedagogy/ui/inlineSegments.js.map +1 -1
- package/lib/pedagogy/ui/pedagogyStyle.d.ts +8 -9
- package/lib/pedagogy/ui/pedagogyStyle.d.ts.map +1 -1
- package/lib/pedagogy/ui/pedagogyStyle.js +10 -18
- package/lib/pedagogy/ui/pedagogyStyle.js.map +1 -1
- package/lib/seo/core/plainDescription.d.ts +2 -2
- package/lib/seo/core/plainDescription.js +5 -2
- package/lib/seo/core/plainDescription.js.map +1 -1
- package/lib/share/core/devices.d.ts +41 -0
- package/lib/share/core/devices.d.ts.map +1 -0
- package/lib/share/core/devices.js +58 -0
- package/lib/share/core/devices.js.map +1 -0
- package/lib/share/core/index.d.ts +4 -0
- package/lib/share/core/index.d.ts.map +1 -1
- package/lib/share/core/index.js +2 -0
- package/lib/share/core/index.js.map +1 -1
- package/lib/share/core/regions.d.ts +43 -0
- package/lib/share/core/regions.d.ts.map +1 -0
- package/lib/share/core/regions.js +71 -0
- package/lib/share/core/regions.js.map +1 -0
- package/lib/share/ui/HiddenPartsProvider.d.ts.map +1 -1
- package/lib/share/ui/HiddenPartsProvider.js +4 -1
- package/lib/share/ui/HiddenPartsProvider.js.map +1 -1
- package/lib/share/ui/PagePart.d.ts.map +1 -1
- package/lib/share/ui/PagePart.js +10 -1
- package/lib/share/ui/PagePart.js.map +1 -1
- package/lib/share/ui/ShareButton.d.ts.map +1 -1
- package/lib/share/ui/ShareButton.js +3 -4
- package/lib/share/ui/ShareButton.js.map +1 -1
- package/lib/share/ui/ShareConfigOptions.d.ts +6 -1
- package/lib/share/ui/ShareConfigOptions.d.ts.map +1 -1
- package/lib/share/ui/ShareConfigOptions.js +6 -13
- package/lib/share/ui/ShareConfigOptions.js.map +1 -1
- package/lib/share/ui/ShareDialog.d.ts +14 -0
- package/lib/share/ui/ShareDialog.d.ts.map +1 -1
- package/lib/share/ui/ShareDialog.js +3 -3
- package/lib/share/ui/ShareDialog.js.map +1 -1
- package/lib/share/ui/ShareDialogContent.d.ts +2 -2
- package/lib/share/ui/ShareDialogContent.d.ts.map +1 -1
- package/lib/share/ui/ShareDialogContent.js +36 -30
- package/lib/share/ui/ShareDialogContent.js.map +1 -1
- package/lib/share/ui/ShareLinkBar.d.ts +21 -0
- package/lib/share/ui/ShareLinkBar.d.ts.map +1 -0
- package/lib/share/ui/ShareLinkBar.js +32 -0
- package/lib/share/ui/ShareLinkBar.js.map +1 -0
- package/lib/share/ui/SharePanes.d.ts +17 -0
- package/lib/share/ui/SharePanes.d.ts.map +1 -0
- package/lib/share/ui/SharePanes.js +27 -0
- package/lib/share/ui/SharePanes.js.map +1 -0
- package/lib/share/ui/SharePartOptions.d.ts +9 -3
- package/lib/share/ui/SharePartOptions.d.ts.map +1 -1
- package/lib/share/ui/SharePartOptions.js +12 -16
- package/lib/share/ui/SharePartOptions.js.map +1 -1
- package/lib/share/ui/SharePartRow.d.ts +37 -0
- package/lib/share/ui/SharePartRow.d.ts.map +1 -0
- package/lib/share/ui/SharePartRow.js +25 -0
- package/lib/share/ui/SharePartRow.js.map +1 -0
- package/lib/share/ui/SharePresetTabs.d.ts +10 -7
- package/lib/share/ui/SharePresetTabs.d.ts.map +1 -1
- package/lib/share/ui/SharePresetTabs.js +17 -17
- package/lib/share/ui/SharePresetTabs.js.map +1 -1
- package/lib/share/ui/SharePreview.d.ts +32 -0
- package/lib/share/ui/SharePreview.d.ts.map +1 -0
- package/lib/share/ui/SharePreview.js +129 -0
- package/lib/share/ui/SharePreview.js.map +1 -0
- package/lib/share/ui/SharePreviewDevices.d.ts +20 -0
- package/lib/share/ui/SharePreviewDevices.d.ts.map +1 -0
- package/lib/share/ui/SharePreviewDevices.js +40 -0
- package/lib/share/ui/SharePreviewDevices.js.map +1 -0
- package/lib/share/ui/index.d.ts +1 -0
- package/lib/share/ui/index.d.ts.map +1 -1
- package/lib/share/ui/index.js.map +1 -1
- package/lib/share/ui/shareMarking.d.ts +14 -0
- package/lib/share/ui/shareMarking.d.ts.map +1 -0
- package/lib/share/ui/shareMarking.js +17 -0
- package/lib/share/ui/shareMarking.js.map +1 -0
- package/lib/share/ui/useShareRegionReports.d.ts +12 -0
- package/lib/share/ui/useShareRegionReports.d.ts.map +1 -0
- package/lib/share/ui/useShareRegionReports.js +102 -0
- package/lib/share/ui/useShareRegionReports.js.map +1 -0
- package/lib/shared/ui/ActionTooltip.d.ts +42 -0
- package/lib/shared/ui/ActionTooltip.d.ts.map +1 -0
- package/lib/shared/ui/ActionTooltip.js +21 -0
- package/lib/shared/ui/ActionTooltip.js.map +1 -0
- package/lib/shared/ui/index.d.ts +4 -0
- package/lib/shared/ui/index.d.ts.map +1 -1
- package/lib/shared/ui/index.js +2 -0
- package/lib/shared/ui/index.js.map +1 -1
- package/lib/shared/ui/useActionTooltip.d.ts +48 -0
- package/lib/shared/ui/useActionTooltip.d.ts.map +1 -0
- package/lib/shared/ui/useActionTooltip.js +78 -0
- package/lib/shared/ui/useActionTooltip.js.map +1 -0
- package/lib/structure/core/index.d.ts +2 -0
- package/lib/structure/core/index.d.ts.map +1 -1
- package/lib/structure/core/index.js +1 -0
- package/lib/structure/core/index.js.map +1 -1
- package/lib/structure/core/sourceMolecule.d.ts +18 -0
- package/lib/structure/core/sourceMolecule.d.ts.map +1 -0
- package/lib/structure/core/sourceMolecule.js +26 -0
- package/lib/structure/core/sourceMolecule.js.map +1 -0
- package/lib/structure/core/structureNotations.d.ts +57 -0
- package/lib/structure/core/structureNotations.d.ts.map +1 -0
- package/lib/structure/core/structureNotations.js +74 -0
- package/lib/structure/core/structureNotations.js.map +1 -0
- package/lib/structure/core/structurePicture.d.ts +49 -0
- package/lib/structure/core/structurePicture.d.ts.map +1 -0
- package/lib/structure/core/structurePicture.js +83 -0
- package/lib/structure/core/structurePicture.js.map +1 -0
- package/lib/structure/ui/EditorExportButton.d.ts +23 -0
- package/lib/structure/ui/EditorExportButton.d.ts.map +1 -0
- package/lib/structure/ui/EditorExportButton.js +23 -0
- package/lib/structure/ui/EditorExportButton.js.map +1 -0
- package/lib/structure/ui/EditorHelpButton.d.ts +5 -2
- package/lib/structure/ui/EditorHelpButton.d.ts.map +1 -1
- package/lib/structure/ui/EditorHelpButton.js +8 -9
- package/lib/structure/ui/EditorHelpButton.js.map +1 -1
- package/lib/structure/ui/StructureEditor.d.ts +8 -0
- package/lib/structure/ui/StructureEditor.d.ts.map +1 -1
- package/lib/structure/ui/StructureEditor.js +26 -3
- package/lib/structure/ui/StructureEditor.js.map +1 -1
- package/lib/structure/ui/StructureExportDialog.d.ts +34 -0
- package/lib/structure/ui/StructureExportDialog.d.ts.map +1 -0
- package/lib/structure/ui/StructureExportDialog.js +43 -0
- package/lib/structure/ui/StructureExportDialog.js.map +1 -0
- package/lib/structure/ui/StructureExportPanel.d.ts +37 -0
- package/lib/structure/ui/StructureExportPanel.d.ts.map +1 -0
- package/lib/structure/ui/StructureExportPanel.js +75 -0
- package/lib/structure/ui/StructureExportPanel.js.map +1 -0
- package/lib/structure/ui/StructureNotationRow.d.ts +22 -0
- package/lib/structure/ui/StructureNotationRow.d.ts.map +1 -0
- package/lib/structure/ui/StructureNotationRow.js +34 -0
- package/lib/structure/ui/StructureNotationRow.js.map +1 -0
- package/lib/structure/ui/StructurePicturePane.d.ts +23 -0
- package/lib/structure/ui/StructurePicturePane.d.ts.map +1 -0
- package/lib/structure/ui/StructurePicturePane.js +113 -0
- package/lib/structure/ui/StructurePicturePane.js.map +1 -0
- package/lib/structure/ui/StructureSvg.d.ts.map +1 -1
- package/lib/structure/ui/StructureSvg.js +2 -18
- package/lib/structure/ui/StructureSvg.js.map +1 -1
- package/lib/structure/ui/ToolbarTooltip.d.ts.map +1 -1
- package/lib/structure/ui/ToolbarTooltip.js +1 -2
- package/lib/structure/ui/ToolbarTooltip.js.map +1 -1
- package/lib/structure/ui/index.d.ts +7 -3
- package/lib/structure/ui/index.d.ts.map +1 -1
- package/lib/structure/ui/index.js +5 -3
- package/lib/structure/ui/index.js.map +1 -1
- package/lib/structure/ui/structureNotationText.d.ts +23 -0
- package/lib/structure/ui/structureNotationText.d.ts.map +1 -0
- package/lib/structure/ui/structureNotationText.js +42 -0
- package/lib/structure/ui/structureNotationText.js.map +1 -0
- package/lib/structure/ui/useEditorStructure.d.ts +37 -0
- package/lib/structure/ui/useEditorStructure.d.ts.map +1 -0
- package/lib/structure/ui/useEditorStructure.js +70 -0
- package/lib/structure/ui/useEditorStructure.js.map +1 -0
- package/package.json +1 -1
- package/src/chart/ui/Axes.tsx +53 -5
- package/src/chrome/ui/NavMenuButton.tsx +61 -18
- package/src/chrome/ui/SiteHeader.tsx +37 -1
- package/src/chrome/ui/navItem.ts +7 -0
- package/src/citation/ui/entries.tsx +10 -2
- package/src/clipboard/ui/ClickToCopy.tsx +47 -13
- package/src/clipboard/ui/CopyButton.tsx +4 -2
- package/src/clipboard/ui/CopyableValue.tsx +88 -13
- package/src/clipboard/ui/index.ts +5 -1
- package/src/confirm/ui/ConfirmButton.tsx +2 -0
- package/src/delimited/ui/TableDataButton.tsx +2 -1
- package/src/download/core/figureSvg.ts +42 -10
- package/src/help/ui/HelpToolbarButton.tsx +2 -1
- package/src/locales/de.json +39 -4
- package/src/locales/en.json +39 -4
- package/src/locales/es.json +39 -4
- package/src/locales/fr.json +39 -4
- package/src/locales/it.json +39 -4
- package/src/number/ui/NumberInput.tsx +1 -1
- package/src/overlay/ui/OverlayIconButton.tsx +1 -0
- package/src/panel/ui/ClearButton.tsx +1 -0
- package/src/pedagogy/core/inlineMarks.ts +15 -7
- package/src/pedagogy/ui/GlossaryDefinition.tsx +8 -18
- package/src/pedagogy/ui/GlossaryIndex.tsx +0 -1
- package/src/pedagogy/ui/GlossaryProvider.tsx +10 -2
- package/src/pedagogy/ui/GlossaryText.tsx +10 -2
- package/src/pedagogy/ui/InlineText.tsx +21 -4
- package/src/pedagogy/ui/ReferenceSectionBlock.tsx +8 -2
- package/src/pedagogy/ui/SyntaxTooltip.tsx +11 -9
- package/src/pedagogy/ui/TutorialStepStrip.tsx +50 -18
- package/src/pedagogy/ui/glossaryContext.ts +7 -0
- package/src/pedagogy/ui/index.ts +1 -1
- package/src/pedagogy/ui/inlineSegments.tsx +7 -0
- package/src/pedagogy/ui/pedagogyStyle.ts +12 -24
- package/src/seo/core/plainDescription.ts +4 -2
- package/src/share/core/devices.ts +80 -0
- package/src/share/core/index.ts +14 -0
- package/src/share/core/regions.ts +87 -0
- package/src/share/ui/HiddenPartsProvider.tsx +6 -1
- package/src/share/ui/PagePart.tsx +12 -1
- package/src/share/ui/ShareButton.tsx +3 -6
- package/src/share/ui/ShareConfigOptions.tsx +25 -16
- package/src/share/ui/ShareDialog.tsx +17 -3
- package/src/share/ui/ShareDialogContent.tsx +80 -77
- package/src/share/ui/ShareLinkBar.tsx +70 -0
- package/src/share/ui/SharePanes.tsx +62 -0
- package/src/share/ui/SharePartOptions.tsx +25 -26
- package/src/share/ui/SharePartRow.tsx +78 -0
- package/src/share/ui/SharePresetTabs.tsx +36 -43
- package/src/share/ui/SharePreview.tsx +235 -0
- package/src/share/ui/SharePreviewDevices.tsx +70 -0
- package/src/share/ui/index.ts +1 -0
- package/src/share/ui/shareMarking.ts +18 -0
- package/src/share/ui/useShareRegionReports.ts +108 -0
- package/src/shared/ui/ActionTooltip.tsx +70 -0
- package/src/shared/ui/index.ts +7 -0
- package/src/shared/ui/useActionTooltip.ts +103 -0
- package/src/structure/core/index.ts +5 -0
- package/src/structure/core/sourceMolecule.ts +26 -0
- package/src/structure/core/structureNotations.ts +125 -0
- package/src/structure/core/structurePicture.ts +119 -0
- package/src/structure/ui/EditorExportButton.tsx +62 -0
- package/src/structure/ui/EditorHelpButton.tsx +26 -30
- package/src/structure/ui/StructureEditor.tsx +51 -5
- package/src/structure/ui/StructureExportDialog.tsx +90 -0
- package/src/structure/ui/StructureExportPanel.tsx +156 -0
- package/src/structure/ui/StructureNotationRow.tsx +74 -0
- package/src/structure/ui/StructurePicturePane.tsx +208 -0
- package/src/structure/ui/StructureSvg.tsx +3 -12
- package/src/structure/ui/ToolbarTooltip.tsx +1 -1
- package/src/structure/ui/index.ts +7 -3
- package/src/structure/ui/structureNotationText.ts +56 -0
- package/src/structure/ui/useEditorStructure.ts +119 -0
- package/styles/chrome.css +414 -14
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a tooltip on a control that is pressed should be on screen.
|
|
3
|
+
*
|
|
4
|
+
* Such a control is where a hover tooltip goes wrong, in two ways that look
|
|
5
|
+
* like one. What it opens lands over the control, so the `mouseleave` that
|
|
6
|
+
* would close the card is never delivered and the card is left on screen, over
|
|
7
|
+
* the thing it was explaining. And the open is *scheduled*: a press within the
|
|
8
|
+
* hover delay cancels nothing, so the card appears after the press, outlives
|
|
9
|
+
* what was opened, and comes back when it is closed — with the pointer long
|
|
10
|
+
* gone.
|
|
11
|
+
*
|
|
12
|
+
* Blueprint's own hover handling cannot be told either of those, so the state
|
|
13
|
+
* is held here and Blueprint is told only when to draw.
|
|
14
|
+
*
|
|
15
|
+
* The card is a pointer affordance only, and deliberately does not open on
|
|
16
|
+
* focus: what a control opens hands the focus back as it closes, which would
|
|
17
|
+
* put the card on screen with the pointer long gone. Nothing is lost by it —
|
|
18
|
+
* the control carries the same words as its accessible name, which is what a
|
|
19
|
+
* screen reader reads.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { useEffect, useRef, useState } from 'react';
|
|
23
|
+
|
|
24
|
+
/** The handlers the target carries, which decide when the card is drawn. */
|
|
25
|
+
export interface ActionTooltipTarget {
|
|
26
|
+
onPointerEnter: () => void;
|
|
27
|
+
onPointerLeave: () => void;
|
|
28
|
+
onPointerDownCapture: () => void;
|
|
29
|
+
onClickCapture: () => void;
|
|
30
|
+
onBlurCapture: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Whether the card is drawn, and what decides it. */
|
|
34
|
+
export interface ActionTooltipState {
|
|
35
|
+
/** What to give the tooltip as `isOpen`. */
|
|
36
|
+
isOpen: boolean;
|
|
37
|
+
/** What to give the target, or the element wrapping it. */
|
|
38
|
+
target: ActionTooltipTarget;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Long enough that sweeping the pointer across a row of controls opens none. */
|
|
42
|
+
export const ACTION_TOOLTIP_DELAY = 250;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Hold a tooltip's hover, so a press closes it and nothing reopens it until
|
|
46
|
+
* the pointer leaves the control and comes back.
|
|
47
|
+
* @param opened - Whether what the control opens is on screen; while it is,
|
|
48
|
+
* the card stays shut.
|
|
49
|
+
* @param delay - How long the pointer rests before the card is drawn, in
|
|
50
|
+
* milliseconds. A card heavier than a line of text asks for longer.
|
|
51
|
+
* @returns Whether to draw the card, and the handlers that decide it.
|
|
52
|
+
*/
|
|
53
|
+
export function useActionTooltip(
|
|
54
|
+
opened = false,
|
|
55
|
+
delay = ACTION_TOOLTIP_DELAY,
|
|
56
|
+
): ActionTooltipState {
|
|
57
|
+
const [shown, setShown] = useState(false);
|
|
58
|
+
const waiting = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
59
|
+
// Set by the press, and cleared only by the pointer actually leaving. It is
|
|
60
|
+
// what the pointer never leaving looks like: the dialog lands over the
|
|
61
|
+
// control, and removing it again makes the browser hand the control a fresh
|
|
62
|
+
// `pointerenter` — with the pointer never having moved. Clearing the latch
|
|
63
|
+
// there, or on the focus the dialog gives back as it closes, is what made
|
|
64
|
+
// the card reappear the moment the dialog was dismissed.
|
|
65
|
+
const pressed = useRef(false);
|
|
66
|
+
|
|
67
|
+
function cancel(): void {
|
|
68
|
+
if (waiting.current !== null) clearTimeout(waiting.current);
|
|
69
|
+
waiting.current = null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function hide(): void {
|
|
73
|
+
cancel();
|
|
74
|
+
setShown(false);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function press(): void {
|
|
78
|
+
pressed.current = true;
|
|
79
|
+
hide();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
useEffect(() => cancel, []);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
isOpen: shown && !opened,
|
|
86
|
+
target: {
|
|
87
|
+
onPointerEnter: () => {
|
|
88
|
+
if (pressed.current) return;
|
|
89
|
+
cancel();
|
|
90
|
+
waiting.current = setTimeout(() => setShown(true), delay);
|
|
91
|
+
},
|
|
92
|
+
onPointerLeave: () => {
|
|
93
|
+
pressed.current = false;
|
|
94
|
+
hide();
|
|
95
|
+
},
|
|
96
|
+
onPointerDownCapture: press,
|
|
97
|
+
// Enter and Space fire no pointer event, so a control worked from the
|
|
98
|
+
// keyboard is let go of here instead.
|
|
99
|
+
onClickCapture: press,
|
|
100
|
+
onBlurCapture: hide,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -72,6 +72,11 @@ export type { ReadStructureResult, StructureKind } from './readStructure.ts';
|
|
|
72
72
|
export { looksLikeSmarts, readStructure } from './readStructure.ts';
|
|
73
73
|
export type { StructureError } from './structureError.ts';
|
|
74
74
|
export { structureError } from './structureError.ts';
|
|
75
|
+
export type {
|
|
76
|
+
StructurePicture,
|
|
77
|
+
StructurePictureOptions,
|
|
78
|
+
} from './structurePicture.ts';
|
|
79
|
+
export { STRUCTURE_BOND_LENGTH, structurePicture } from './structurePicture.ts';
|
|
75
80
|
export type {
|
|
76
81
|
StructureSource,
|
|
77
82
|
StructureSourceInput,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the notation a depiction was handed into the molecule it draws.
|
|
3
|
+
*
|
|
4
|
+
* **Not exported from `core/index.ts`**: it value-imports `openchemlib`, so a
|
|
5
|
+
* barrel carrying it would pull the whole library into every site that imports
|
|
6
|
+
* `react-cheminfo/core`. The depiction and the export dialog both load it
|
|
7
|
+
* lazily, which is where openchemlib already is.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Molecule } from 'openchemlib';
|
|
11
|
+
|
|
12
|
+
import type { StructureSource } from './structureSource.ts';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse one picked notation.
|
|
16
|
+
* @param source - The notation, as `structureSource` picked it.
|
|
17
|
+
* @returns The molecule, which holds no atom for an empty source.
|
|
18
|
+
* @throws {Error} Whatever OpenChemLib throws on a notation it cannot read.
|
|
19
|
+
*/
|
|
20
|
+
export function sourceMolecule(source: StructureSource): Molecule {
|
|
21
|
+
const { kind, value, coordinates } = source;
|
|
22
|
+
if (kind === 'idcode') return Molecule.fromIDCode(value, coordinates);
|
|
23
|
+
if (kind === 'molfile') return Molecule.fromMolfile(value);
|
|
24
|
+
if (kind === 'smiles') return Molecule.fromSmiles(value);
|
|
25
|
+
return new Molecule(0, 0);
|
|
26
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every way this package writes a drawn structure out.
|
|
3
|
+
*
|
|
4
|
+
* **Not exported from `core/index.ts`, and it must not be.** It is the one
|
|
5
|
+
* structure module that value-imports `openchemlib`, because the canonical
|
|
6
|
+
* forms below are `CanonizerUtil`'s and nothing on `Molecule` reaches them, so
|
|
7
|
+
* a barrel carrying it would pull openchemlib into every site that imports
|
|
8
|
+
* `react-cheminfo/core` for a formatter. The export dialog loads it lazily
|
|
9
|
+
* instead, next to the editor that already has openchemlib.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Molecule } from 'openchemlib';
|
|
13
|
+
import { CanonizerUtil } from 'openchemlib';
|
|
14
|
+
|
|
15
|
+
/** The notations offered, in the order they are offered in. */
|
|
16
|
+
export type StructureNotationKind =
|
|
17
|
+
| 'smiles'
|
|
18
|
+
| 'molfileV3000'
|
|
19
|
+
| 'molfileV2000'
|
|
20
|
+
| 'idCode'
|
|
21
|
+
| 'noStereoIdCode'
|
|
22
|
+
| 'noStereoTautomerIdCode';
|
|
23
|
+
|
|
24
|
+
/** How a notation leaves the page as a file. */
|
|
25
|
+
export interface StructureNotationFile {
|
|
26
|
+
/** Added to the structure's own name, so two molfiles do not collide. */
|
|
27
|
+
suffix: string;
|
|
28
|
+
/** The extension, without its dot. */
|
|
29
|
+
extension: string;
|
|
30
|
+
/** What the file holds, so the system opens it with the right application. */
|
|
31
|
+
mimeType: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** One way of writing a structure. */
|
|
35
|
+
export interface StructureNotation {
|
|
36
|
+
/** Which notation it is. */
|
|
37
|
+
kind: StructureNotationKind;
|
|
38
|
+
/** The notation itself, empty when OpenChemLib would not write it. */
|
|
39
|
+
value: string;
|
|
40
|
+
/**
|
|
41
|
+
* Whether it is one of the canonical identifiers — a string that names the
|
|
42
|
+
* molecule rather than describes the drawing, and that a record is looked up
|
|
43
|
+
* by. They answer a database question rather than a drawing one, so they are
|
|
44
|
+
* shown apart from the rest.
|
|
45
|
+
*/
|
|
46
|
+
canonical: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The file it is saved as. Absent for a notation that is only ever copied.
|
|
49
|
+
* @default undefined
|
|
50
|
+
*/
|
|
51
|
+
file?: StructureNotationFile;
|
|
52
|
+
/**
|
|
53
|
+
* Whether it runs over several lines, so it is shown as a block rather than
|
|
54
|
+
* on the label's own line.
|
|
55
|
+
*/
|
|
56
|
+
block: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** What a molfile is, for the application that opens one. */
|
|
60
|
+
const MOLFILE_TYPE = 'chemical/x-mdl-molfile';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Write a structure every way the export dialog offers.
|
|
64
|
+
*
|
|
65
|
+
* A notation OpenChemLib refuses — a query fragment has no SMILES, a generic
|
|
66
|
+
* tautomer cannot always be canonized — comes back empty rather than taking
|
|
67
|
+
* the rest of the list down with it.
|
|
68
|
+
* @param molecule - The structure, as the editor handed it over. Not modified.
|
|
69
|
+
* @returns One entry per notation, in the order they are shown.
|
|
70
|
+
*/
|
|
71
|
+
export function structureNotations(molecule: Molecule): StructureNotation[] {
|
|
72
|
+
const idCode = written(() => molecule.getIDCode());
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
kind: 'smiles',
|
|
76
|
+
value: written(() => molecule.toIsomericSmiles()),
|
|
77
|
+
canonical: false,
|
|
78
|
+
block: false,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
kind: 'molfileV3000',
|
|
82
|
+
value: written(() => molecule.toMolfileV3()),
|
|
83
|
+
canonical: false,
|
|
84
|
+
file: { suffix: '-v3000', extension: 'mol', mimeType: MOLFILE_TYPE },
|
|
85
|
+
block: true,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
kind: 'molfileV2000',
|
|
89
|
+
value: written(() => molecule.toMolfile()),
|
|
90
|
+
canonical: false,
|
|
91
|
+
file: { suffix: '-v2000', extension: 'mol', mimeType: MOLFILE_TYPE },
|
|
92
|
+
block: true,
|
|
93
|
+
},
|
|
94
|
+
{ kind: 'idCode', value: idCode, canonical: true, block: false },
|
|
95
|
+
{
|
|
96
|
+
kind: 'noStereoIdCode',
|
|
97
|
+
value: written(() =>
|
|
98
|
+
CanonizerUtil.getIDCode(molecule, CanonizerUtil.NOSTEREO),
|
|
99
|
+
),
|
|
100
|
+
canonical: true,
|
|
101
|
+
block: false,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
kind: 'noStereoTautomerIdCode',
|
|
105
|
+
value: written(() =>
|
|
106
|
+
CanonizerUtil.getIDCode(molecule, CanonizerUtil.NOSTEREO_TAUTOMER),
|
|
107
|
+
),
|
|
108
|
+
canonical: true,
|
|
109
|
+
block: false,
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* One notation, or nothing when OpenChemLib will not write it.
|
|
116
|
+
* @param write - What writes it.
|
|
117
|
+
* @returns The notation, empty on a refusal.
|
|
118
|
+
*/
|
|
119
|
+
function written(write: () => string): string {
|
|
120
|
+
try {
|
|
121
|
+
return write();
|
|
122
|
+
} catch {
|
|
123
|
+
return '';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A structure as a picture, drawn at the size it is saved at.
|
|
3
|
+
*
|
|
4
|
+
* The resolution is a bond length rather than a canvas: OpenChemLib caps the
|
|
5
|
+
* bond it draws at 24 pixels whatever box it is given, so asking for a canvas
|
|
6
|
+
* four times larger produces the same small drawing with more white around it
|
|
7
|
+
* — a resolution control that changes the file and not the picture. Lifting
|
|
8
|
+
* the cap and cropping to the structure is what makes `4×` genuinely four
|
|
9
|
+
* times the drawing.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Molecule } from 'openchemlib';
|
|
13
|
+
|
|
14
|
+
import type { FigurePixels } from '../../download/core/figureScale.ts';
|
|
15
|
+
|
|
16
|
+
/** How the picture is drawn. */
|
|
17
|
+
export interface StructurePictureOptions {
|
|
18
|
+
/**
|
|
19
|
+
* The longest bond drawn, in pixels, which is what sets the size of
|
|
20
|
+
* everything else — the picture is cropped to the structure afterwards.
|
|
21
|
+
* @default 60
|
|
22
|
+
*/
|
|
23
|
+
bondLength?: number;
|
|
24
|
+
/**
|
|
25
|
+
* What is painted behind the structure. An SVG left unpainted opens
|
|
26
|
+
* transparent, and a structure is drawn in black, so it disappears in
|
|
27
|
+
* anything with a dark background — a slide, a reader in dark mode.
|
|
28
|
+
* @default '#ffffff'
|
|
29
|
+
*/
|
|
30
|
+
background?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A structure drawn, and how big it came out. */
|
|
34
|
+
export interface StructurePicture extends FigurePixels {
|
|
35
|
+
/** The SVG document, ready to be saved or rasterized. */
|
|
36
|
+
markup: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The bond length a picture is drawn at before the resolution multiplies it.
|
|
41
|
+
*
|
|
42
|
+
* Chosen so a drug-sized molecule comes out around 300 pixels wide, which is
|
|
43
|
+
* what a note or a chat window wants; two or three times that is a slide, and
|
|
44
|
+
* four is print.
|
|
45
|
+
*/
|
|
46
|
+
export const STRUCTURE_BOND_LENGTH = 60;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The canvas the structure is laid out in before it is cropped out of it.
|
|
50
|
+
*
|
|
51
|
+
* Only a ceiling: past it the box, rather than the bond length, would decide
|
|
52
|
+
* the size, and it is far beyond what a browser will paint anyway.
|
|
53
|
+
*/
|
|
54
|
+
const CANVAS = 20_000;
|
|
55
|
+
|
|
56
|
+
/** Where the opening `<svg …>` tag ends, so the background goes after it. */
|
|
57
|
+
const OPEN_TAG = /<svg\b[^>]*>/u;
|
|
58
|
+
|
|
59
|
+
/** The size OpenChemLib wrote on the document it produced. */
|
|
60
|
+
const SIDE = /\b(?<side>width|height)="(?<pixels>\d+)px"/gu;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Draw a structure as a standalone SVG document, cropped to the structure.
|
|
64
|
+
* @param molecule - The structure. Not modified.
|
|
65
|
+
* @param options - How large to draw it, and what to paint behind it.
|
|
66
|
+
* @returns The document and the size it came out at.
|
|
67
|
+
*/
|
|
68
|
+
export function structurePicture(
|
|
69
|
+
molecule: Molecule,
|
|
70
|
+
options: StructurePictureOptions = {},
|
|
71
|
+
): StructurePicture {
|
|
72
|
+
const { bondLength = STRUCTURE_BOND_LENGTH, background = '#ffffff' } =
|
|
73
|
+
options;
|
|
74
|
+
const markup = molecule.toSVG(CANVAS, CANVAS, undefined, {
|
|
75
|
+
autoCrop: true,
|
|
76
|
+
autoCropMargin: Math.max(2, Math.round(bondLength / 6)),
|
|
77
|
+
maxAVBL: Math.max(1, Math.round(bondLength)),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return { markup: painted(markup, background), ...sizeOf(markup) };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Paint a background into the document, so the file does not open
|
|
85
|
+
* transparent.
|
|
86
|
+
* @param markup - The document as OpenChemLib wrote it.
|
|
87
|
+
* @param background - What to paint, or `transparent` to paint nothing.
|
|
88
|
+
* @returns The document.
|
|
89
|
+
*/
|
|
90
|
+
function painted(markup: string, background: string): string {
|
|
91
|
+
if (background === 'transparent') return markup;
|
|
92
|
+
const open = OPEN_TAG.exec(markup);
|
|
93
|
+
// A document whose opening tag cannot be found is handed back unpainted
|
|
94
|
+
// rather than corrupted by an insertion at a guessed offset.
|
|
95
|
+
if (open === null) return markup;
|
|
96
|
+
const end = open.index + open[0].length;
|
|
97
|
+
return `${markup.slice(0, end)}<rect width="100%" height="100%" fill="${background}" />${markup.slice(end)}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* How big the cropped document came out.
|
|
102
|
+
*
|
|
103
|
+
* Read off the document rather than computed, because the crop is the
|
|
104
|
+
* structure's own shape: two molecules asked for at the same bond length come
|
|
105
|
+
* out at different sizes, and the size is what the reader is shown.
|
|
106
|
+
* @param markup - The document as OpenChemLib wrote it.
|
|
107
|
+
* @returns Its size in pixels.
|
|
108
|
+
*/
|
|
109
|
+
function sizeOf(markup: string): FigurePixels {
|
|
110
|
+
const sides: Record<string, number> = {};
|
|
111
|
+
SIDE.lastIndex = 0;
|
|
112
|
+
for (const match of markup.matchAll(SIDE)) {
|
|
113
|
+
const { side, pixels } = match.groups ?? {};
|
|
114
|
+
if (side !== undefined && pixels !== undefined && !(side in sides)) {
|
|
115
|
+
sides[side] = Number(pixels);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { width: sides.width ?? CANVAS, height: sides.height ?? CANVAS };
|
|
119
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Button } from '@blueprintjs/core';
|
|
2
|
+
import type { ReactElement } from 'react';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
6
|
+
import { ActionTooltip } from '../../shared/ui/ActionTooltip.tsx';
|
|
7
|
+
import type { StructureSourceInput } from '../core/structureSource.ts';
|
|
8
|
+
|
|
9
|
+
import { StructureExportDialog } from './StructureExportDialog.tsx';
|
|
10
|
+
|
|
11
|
+
/** What {@link EditorExportButton} needs. */
|
|
12
|
+
export interface EditorExportButtonProps {
|
|
13
|
+
/** Reads what is drawn, called the moment the dialog is opened. */
|
|
14
|
+
source: () => StructureSourceInput;
|
|
15
|
+
/** Whether the editor draws a query fragment. */
|
|
16
|
+
fragment: boolean;
|
|
17
|
+
/** Whether the canvas holds nothing to export. */
|
|
18
|
+
empty: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The editor's export button, beside the help one, opening every notation of
|
|
23
|
+
* the drawing and the picture of it.
|
|
24
|
+
*
|
|
25
|
+
* The structure is read when the button is pressed rather than kept beside the
|
|
26
|
+
* canvas, so the dialog opens on what is drawn now and the editor is not
|
|
27
|
+
* re-rendered on every stroke to keep a copy in step.
|
|
28
|
+
* @param props - See {@link EditorExportButtonProps}.
|
|
29
|
+
* @returns The button and its dialog.
|
|
30
|
+
*/
|
|
31
|
+
export function EditorExportButton(
|
|
32
|
+
props: EditorExportButtonProps,
|
|
33
|
+
): ReactElement {
|
|
34
|
+
const { source, fragment, empty } = props;
|
|
35
|
+
const t = useChromeT();
|
|
36
|
+
const [opened, setOpened] = useState<StructureSourceInput | null>(null);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
<ActionTooltip
|
|
41
|
+
content={t('structure.export.open')}
|
|
42
|
+
placement="left"
|
|
43
|
+
opened={opened !== null}
|
|
44
|
+
>
|
|
45
|
+
<Button
|
|
46
|
+
variant="minimal"
|
|
47
|
+
size="small"
|
|
48
|
+
icon="export"
|
|
49
|
+
disabled={empty}
|
|
50
|
+
aria-label={t('structure.export.open')}
|
|
51
|
+
onClick={() => setOpened(source())}
|
|
52
|
+
/>
|
|
53
|
+
</ActionTooltip>
|
|
54
|
+
<StructureExportDialog
|
|
55
|
+
{...opened}
|
|
56
|
+
isOpen={opened !== null}
|
|
57
|
+
fragment={fragment}
|
|
58
|
+
onClose={() => setOpened(null)}
|
|
59
|
+
/>
|
|
60
|
+
</>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Button, PopoverNext
|
|
2
|
-
import type {
|
|
1
|
+
import { Button, PopoverNext } from '@blueprintjs/core';
|
|
2
|
+
import type { ReactElement, RefObject } from 'react';
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
4
|
|
|
5
5
|
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
6
|
+
import { ActionTooltip } from '../../shared/ui/ActionTooltip.tsx';
|
|
6
7
|
|
|
7
8
|
import { StructureEditorHelp } from './StructureEditorHelp.tsx';
|
|
8
9
|
import type { StructureEditorMode } from './editorChange.ts';
|
|
@@ -18,8 +19,11 @@ export interface EditorHelpButtonProps {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
* The editor's help button,
|
|
22
|
-
*
|
|
22
|
+
* The editor's help button, opening the guide to the mouse and the keyboard.
|
|
23
|
+
*
|
|
24
|
+
* It is placed by whoever renders it — the editor puts it in the corner of the
|
|
25
|
+
* drawing, next to the export button — so the two read as one group rather
|
|
26
|
+
* than as two controls that happen to overlap.
|
|
23
27
|
*
|
|
24
28
|
* F1 opens it too: openchemlib binds that key to a help dialog it does not
|
|
25
29
|
* implement on the web.
|
|
@@ -44,32 +48,24 @@ export function EditorHelpButton(props: EditorHelpButtonProps): ReactElement {
|
|
|
44
48
|
}, [containerRef]);
|
|
45
49
|
|
|
46
50
|
return (
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
<PopoverNext
|
|
52
|
+
isOpen={isOpen}
|
|
53
|
+
onInteraction={setIsOpen}
|
|
54
|
+
placement="left-start"
|
|
55
|
+
content={<StructureEditorHelp mode={mode} fragment={fragment} />}
|
|
56
|
+
>
|
|
57
|
+
<ActionTooltip
|
|
58
|
+
content={t('structure.mouseAndKeyboard')}
|
|
59
|
+
placement="left"
|
|
60
|
+
opened={isOpen}
|
|
53
61
|
>
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
icon="help"
|
|
63
|
-
aria-label={t('structure.mouseAndKeyboard')}
|
|
64
|
-
/>
|
|
65
|
-
</Tooltip>
|
|
66
|
-
</PopoverNext>
|
|
67
|
-
</div>
|
|
62
|
+
<Button
|
|
63
|
+
variant="minimal"
|
|
64
|
+
size="small"
|
|
65
|
+
icon="help"
|
|
66
|
+
aria-label={t('structure.mouseAndKeyboard')}
|
|
67
|
+
/>
|
|
68
|
+
</ActionTooltip>
|
|
69
|
+
</PopoverNext>
|
|
68
70
|
);
|
|
69
71
|
}
|
|
70
|
-
|
|
71
|
-
const CORNER_STYLE: CSSProperties = {
|
|
72
|
-
position: 'absolute',
|
|
73
|
-
top: 4,
|
|
74
|
-
right: 4,
|
|
75
|
-
};
|
|
@@ -28,8 +28,14 @@ import type {
|
|
|
28
28
|
StructureEditorChange,
|
|
29
29
|
StructureEditorMode,
|
|
30
30
|
} from './editorChange.ts';
|
|
31
|
+
import { useEditorStructure } from './useEditorStructure.ts';
|
|
31
32
|
import { useToolbarFloor } from './useToolbarFloor.ts';
|
|
32
33
|
|
|
34
|
+
const EditorExportButton = lazy(async () => {
|
|
35
|
+
const module = await import('./EditorExportButton.tsx');
|
|
36
|
+
return { default: module.EditorExportButton };
|
|
37
|
+
});
|
|
38
|
+
|
|
33
39
|
const EditorCanvas = lazy(async () => {
|
|
34
40
|
const module = await import('./EditorCanvas.tsx');
|
|
35
41
|
return { default: module.EditorCanvas };
|
|
@@ -93,6 +99,14 @@ export interface StructureEditorProps {
|
|
|
93
99
|
* @default true
|
|
94
100
|
*/
|
|
95
101
|
help?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Put a button in the corner that opens every notation of the drawing — its
|
|
104
|
+
* SMILES, its molfiles, its canonical identifiers — and the picture of it,
|
|
105
|
+
* ready to copy or to save. A reaction has none of those, so it is never
|
|
106
|
+
* offered one.
|
|
107
|
+
* @default true
|
|
108
|
+
*/
|
|
109
|
+
exportable?: boolean;
|
|
96
110
|
/**
|
|
97
111
|
* Class the container carries, so a site can reach it from its stylesheet.
|
|
98
112
|
* @default undefined
|
|
@@ -123,6 +137,7 @@ export function StructureEditor(props: StructureEditorProps): ReactElement {
|
|
|
123
137
|
minHeight = 320,
|
|
124
138
|
mode = 'molecule',
|
|
125
139
|
help = true,
|
|
140
|
+
exportable = true,
|
|
126
141
|
className,
|
|
127
142
|
style,
|
|
128
143
|
} = props;
|
|
@@ -130,6 +145,16 @@ export function StructureEditor(props: StructureEditorProps): ReactElement {
|
|
|
130
145
|
const t = useChromeT();
|
|
131
146
|
const containerRef = useToolbarFloor({ minHeight, revision });
|
|
132
147
|
const handleChange = useDebounced(onChange, debounce, revision);
|
|
148
|
+
const drawing = useEditorStructure({ value, inputFormat, revision });
|
|
149
|
+
const handleEdit = useCallback(
|
|
150
|
+
(change: StructureEditorChange) => {
|
|
151
|
+
// Tapped undebounced, so a button beside the canvas reads the structure
|
|
152
|
+
// as it stands rather than as it stood before the last burst.
|
|
153
|
+
drawing.track(change);
|
|
154
|
+
handleChange(change);
|
|
155
|
+
},
|
|
156
|
+
[drawing, handleChange],
|
|
157
|
+
);
|
|
133
158
|
|
|
134
159
|
return (
|
|
135
160
|
<div
|
|
@@ -144,22 +169,31 @@ export function StructureEditor(props: StructureEditorProps): ReactElement {
|
|
|
144
169
|
>
|
|
145
170
|
<EditorCanvas
|
|
146
171
|
key={revision}
|
|
147
|
-
onChange={
|
|
172
|
+
onChange={handleEdit}
|
|
148
173
|
fragment={fragment}
|
|
149
174
|
inputFormat={inputFormat}
|
|
150
175
|
value={value}
|
|
151
176
|
mode={mode}
|
|
152
177
|
/>
|
|
153
178
|
{help ? (
|
|
154
|
-
|
|
155
|
-
|
|
179
|
+
<ToolbarTooltip containerRef={containerRef} mode={mode} />
|
|
180
|
+
) : null}
|
|
181
|
+
<div style={CORNER_STYLE}>
|
|
182
|
+
{exportable && mode === 'molecule' ? (
|
|
183
|
+
<EditorExportButton
|
|
184
|
+
source={drawing.source}
|
|
185
|
+
fragment={fragment}
|
|
186
|
+
empty={drawing.empty}
|
|
187
|
+
/>
|
|
188
|
+
) : null}
|
|
189
|
+
{help ? (
|
|
156
190
|
<EditorHelpButton
|
|
157
191
|
containerRef={containerRef}
|
|
158
192
|
mode={mode}
|
|
159
193
|
fragment={fragment}
|
|
160
194
|
/>
|
|
161
|
-
|
|
162
|
-
|
|
195
|
+
) : null}
|
|
196
|
+
</div>
|
|
163
197
|
</Suspense>
|
|
164
198
|
</div>
|
|
165
199
|
);
|
|
@@ -217,6 +251,18 @@ const ROOT_STYLE: CSSProperties = {
|
|
|
217
251
|
background: '#fff',
|
|
218
252
|
};
|
|
219
253
|
|
|
254
|
+
/**
|
|
255
|
+
* The corner the editor's own buttons sit in: export first, then help, so the
|
|
256
|
+
* one that does something to the drawing is the nearer of the two to it.
|
|
257
|
+
*/
|
|
258
|
+
const CORNER_STYLE: CSSProperties = {
|
|
259
|
+
position: 'absolute',
|
|
260
|
+
top: 4,
|
|
261
|
+
right: 4,
|
|
262
|
+
display: 'flex',
|
|
263
|
+
gap: 2,
|
|
264
|
+
};
|
|
265
|
+
|
|
220
266
|
/** The box the canvas will fill, so its arrival moves nothing. */
|
|
221
267
|
const LOADING_STYLE: CSSProperties = {
|
|
222
268
|
position: 'absolute',
|