react-cheminfo 0.75.0 → 0.76.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 (48) hide show
  1. package/lib/download/core/copySvg.d.ts +12 -0
  2. package/lib/download/core/copySvg.d.ts.map +1 -0
  3. package/lib/download/core/copySvg.js +38 -0
  4. package/lib/download/core/copySvg.js.map +1 -0
  5. package/lib/download/core/index.d.ts +1 -0
  6. package/lib/download/core/index.d.ts.map +1 -1
  7. package/lib/download/core/index.js +1 -0
  8. package/lib/download/core/index.js.map +1 -1
  9. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  10. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  11. package/lib/ir/core/bandAssignments.d.ts.map +1 -1
  12. package/lib/ir/core/bandAssignments.js +27 -0
  13. package/lib/ir/core/bandAssignments.js.map +1 -1
  14. package/lib/ir/ui/BandRow.d.ts +4 -4
  15. package/lib/ir/ui/BandRow.d.ts.map +1 -1
  16. package/lib/ir/ui/BandRow.js +41 -14
  17. package/lib/ir/ui/BandRow.js.map +1 -1
  18. package/lib/locales/de.json +2 -0
  19. package/lib/locales/en.json +2 -0
  20. package/lib/locales/es.json +2 -0
  21. package/lib/locales/fr.json +2 -0
  22. package/lib/locales/it.json +2 -0
  23. package/lib/structure/ui/CardStructure.d.ts +43 -0
  24. package/lib/structure/ui/CardStructure.d.ts.map +1 -0
  25. package/lib/structure/ui/CardStructure.js +28 -0
  26. package/lib/structure/ui/CardStructure.js.map +1 -0
  27. package/lib/structure/ui/StructurePicturePane.d.ts +1 -1
  28. package/lib/structure/ui/StructurePicturePane.d.ts.map +1 -1
  29. package/lib/structure/ui/StructurePicturePane.js +5 -2
  30. package/lib/structure/ui/StructurePicturePane.js.map +1 -1
  31. package/lib/structure/ui/index.d.ts +2 -0
  32. package/lib/structure/ui/index.d.ts.map +1 -1
  33. package/lib/structure/ui/index.js +1 -0
  34. package/lib/structure/ui/index.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/download/core/copySvg.ts +40 -0
  37. package/src/download/core/index.ts +1 -0
  38. package/src/ir/core/bandAssignments.ts +27 -0
  39. package/src/ir/ui/BandRow.tsx +84 -26
  40. package/src/locales/de.json +2 -0
  41. package/src/locales/en.json +2 -0
  42. package/src/locales/es.json +2 -0
  43. package/src/locales/fr.json +2 -0
  44. package/src/locales/it.json +2 -0
  45. package/src/structure/ui/CardStructure.tsx +76 -0
  46. package/src/structure/ui/StructurePicturePane.tsx +14 -1
  47. package/src/structure/ui/index.ts +2 -0
  48. package/styles/chrome.css +3 -3
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { OverlayLayer } from "../../overlay/ui/OverlayLayer.js";
3
+ import { structureSource } from "../core/structureSource.js";
4
+ import { Structure } from "./Structure.js";
5
+ import { StructureExportButton } from "./StructureExportButton.js";
6
+ /**
7
+ * A structure of a card, with the glyph that hands it over: the export
8
+ * dialog, which copies it as an SVG or a PNG, a SMILES or a molfile, V2000 or
9
+ * V3000. Every structure an answer draws carries it, alone or in a grid, so
10
+ * none has to be redrawn to be reused.
11
+ *
12
+ * The glyph stands beside the top right corner, faded until the structure is
13
+ * pointed at, as the glyphs of `CardFigure` (`react-cheminfo/card`) do;
14
+ * `styles/card.css` draws both. A structure with nothing to read draws its
15
+ * placeholder alone.
16
+ * @param props - The structure, its size, and the name of its files.
17
+ * @returns The structure and its glyph.
18
+ */
19
+ export function CardStructure(props) {
20
+ const { name, fragment, label, children, ...structure } = props;
21
+ const { idCode, coordinates, molfile, smiles } = structure;
22
+ const drawing = children ?? _jsx(Structure, { ...structure });
23
+ const source = structureSource({ idCode, coordinates, molfile, smiles });
24
+ if (source.kind === 'empty')
25
+ return _jsx(_Fragment, { children: drawing });
26
+ return (_jsxs("div", { className: "card-figure card-structure", children: [_jsx("div", { className: "card-figure__drawing", children: drawing }), _jsx(OverlayLayer, { density: "compact", awake: true, children: _jsx("div", { className: "card-figure__tools", "data-testid": "figure-tools", children: _jsx(StructureExportButton, { idCode: idCode, coordinates: coordinates, molfile: molfile, smiles: smiles, name: name, fragment: fragment, label: label }) }) })] }));
27
+ }
28
+ //# sourceMappingURL=CardStructure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CardStructure.js","sourceRoot":"","sources":["../../../src/structure/ui/CardStructure.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAmC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG7D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAiB,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA6B,CAAC;AA6BpE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,CAAC;IAChE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3D,MAAM,OAAO,GAAG,QAAQ,IAAI,KAAC,SAAS,OAAK,SAAS,GAAI,CAAC;IACzD,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,4BAAG,OAAO,GAAI,CAAC;IAEnD,OAAO,CACL,eAAK,SAAS,EAAC,4BAA4B,aACzC,cAAK,SAAS,EAAC,sBAAsB,YAAE,OAAO,GAAO,EAErD,KAAC,YAAY,IAAC,OAAO,EAAC,SAAS,EAAC,KAAK,kBACnC,cAAK,SAAS,EAAC,oBAAoB,iBAAa,cAAc,YAC5D,KAAC,qBAAqB,IACpB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,GACZ,GACE,GACO,IACX,CACP,CAAC;AACJ,CAAC"}
@@ -9,7 +9,7 @@ export interface StructurePicturePaneProps {
9
9
  }
10
10
  /**
11
11
  * The structure as a picture: what it looks like, how big it leaves, and the
12
- * three ways it leaves.
12
+ * four ways it leaves.
13
13
  *
14
14
  * What is shown is the file: the preview is the very SVG the save button
15
15
  * writes, laid out smaller, so nobody saves a picture they have not seen. The
@@ -1 +1 @@
1
- {"version":3,"file":"StructurePicturePane.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/StructurePicturePane.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAC;AAwBzD,yDAAyD;AACzD,MAAM,WAAW,yBAAyB;IACxC,mCAAmC;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,yBAAyB,GAC/B,YAAY,CAgHd"}
1
+ {"version":3,"file":"StructurePicturePane.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/StructurePicturePane.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAC;AAyBzD,yDAAyD;AACzD,MAAM,WAAW,yBAAyB;IACxC,mCAAmC;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,yBAAyB,GAC/B,YAAY,CA4Hd"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Button, SegmentedControl } from '@blueprintjs/core';
3
3
  import { useMemo, useState } from 'react';
4
4
  import { copyFigure } from "../../download/core/copyFigure.js";
5
+ import { copySvg } from "../../download/core/copySvg.js";
5
6
  import { downloadBlob } from "../../download/core/downloadBlob.js";
6
7
  import { downloadText } from "../../download/core/downloadText.js";
7
8
  import { figurePng } from "../../download/core/figurePng.js";
@@ -16,7 +17,7 @@ import { STRUCTURE_BOND_LENGTH, structurePicture, } from "../core/structurePictu
16
17
  const DEFAULT_SCALE = 2;
17
18
  /**
18
19
  * The structure as a picture: what it looks like, how big it leaves, and the
19
- * three ways it leaves.
20
+ * four ways it leaves.
20
21
  *
21
22
  * What is shown is the file: the preview is the very SVG the save button
22
23
  * writes, laid out smaller, so nobody saves a picture they have not seen. The
@@ -64,7 +65,9 @@ export function StructurePicturePane(props) {
64
65
  }) }), _jsx(Button, { icon: "download", size: "small", intent: "primary", text: t('structure.export.savePng'), onClick: () => void run(async () => {
65
66
  downloadBlob(await png(), `${fileName}.png`);
66
67
  return null;
67
- }) }), _jsx(Button, { icon: "clipboard", size: "small", text: t('structure.export.copyPng'), onClick: () =>
68
+ }) }), _jsx(Button, { icon: "clipboard", size: "small", text: t('structure.export.copySvg'), onClick: () => void run(async () => (await copySvg(picture.markup))
69
+ ? t('structure.export.svgCopied')
70
+ : t('clipboard.failed')) }), _jsx(Button, { icon: "clipboard", size: "small", text: t('structure.export.copyPng'), onClick: () =>
68
71
  // Safari only allows a clipboard write the click itself started,
69
72
  // so the image is handed over as a promise rather than awaited
70
73
  // first — see `copyFigure`.
@@ -1 +1 @@
1
- {"version":3,"file":"StructurePicturePane.js","sourceRoot":"","sources":["../../../src/structure/ui/StructurePicturePane.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StructurePicturePane.js","sourceRoot":"","sources":["../../../src/structure/ui/StructurePicturePane.tsx"],"names":[],"mappings":"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"}
@@ -27,6 +27,8 @@ export type { StructureExportDialogProps } from './StructureExportDialog.tsx';
27
27
  export { StructureExportDialog } from './StructureExportDialog.tsx';
28
28
  export type { StructureExportButtonProps } from './StructureExportButton.tsx';
29
29
  export { StructureExportButton } from './StructureExportButton.tsx';
30
+ export type { CardStructureProps } from './CardStructure.tsx';
31
+ export { CardStructure } from './CardStructure.tsx';
30
32
  export type { StructureExportPanelProps } from './StructureExportPanel.tsx';
31
33
  export type { ToolbarFloorOptions } from './useToolbarFloor.ts';
32
34
  export { useToolbarFloor } from './useToolbarFloor.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -18,6 +18,7 @@ export { StructureSearchDialog } from "./StructureSearchDialog.js";
18
18
  export { useStructureSearchModes } from "./useStructureSearchModes.js";
19
19
  export { StructureExportDialog } from "./StructureExportDialog.js";
20
20
  export { StructureExportButton } from "./StructureExportButton.js";
21
+ export { CardStructure } from "./CardStructure.js";
21
22
  export { useToolbarFloor } from "./useToolbarFloor.js";
22
23
  export { useStructureDraft } from "./useStructureDraft.js";
23
24
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAiB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAuB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAqB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA2B,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA6B,CAAC;AAEpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA6B,CAAC;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA6B,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAiB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAuB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAqB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA2B,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA6B,CAAC;AAEpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA6B,CAAC;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA6B,CAAC;AAEpE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAqB,CAAC;AAGpD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-cheminfo",
3
- "version": "0.75.0",
3
+ "version": "0.76.0",
4
4
  "license": "MIT",
5
5
  "description": "React components, hooks and framework-free helpers shared by the cheminfo sites: site chrome, citations, sharing, routing, pedagogy, charts, and the structure, orbital and 3D molecule viewers",
6
6
  "author": "Luc Patiny",
@@ -0,0 +1,40 @@
1
+ import { writeClipboardItem } from '../../clipboard/core/clipboardItem.ts';
2
+ import { writeToClipboard } from '../../clipboard/core/writeToClipboard.ts';
3
+
4
+ /** The type a picture is put on the clipboard as: no `charset`, which it refuses. */
5
+ const SVG_CLIPBOARD_TYPE = 'image/svg+xml';
6
+
7
+ /**
8
+ * Put an SVG document on the clipboard, without ever throwing at the caller.
9
+ *
10
+ * Where the browser takes an SVG picture, the document goes on as one and as
11
+ * its text, in one item: a slide or a vector editor pastes the drawing, a text
12
+ * editor the markup. Elsewhere — Firefox and Safari take no SVG picture — the
13
+ * text alone, which a vector editor still pastes as a drawing.
14
+ * @param markup - The SVG document.
15
+ * @returns Whether the clipboard now holds it, at least as text.
16
+ */
17
+ export async function copySvg(markup: string): Promise<boolean> {
18
+ if (takesSvgPicture()) {
19
+ const written = await writeClipboardItem({
20
+ [SVG_CLIPBOARD_TYPE]: new Blob([markup], { type: SVG_CLIPBOARD_TYPE }),
21
+ 'text/plain': new Blob([markup], { type: 'text/plain' }),
22
+ });
23
+ if (written) return true;
24
+ }
25
+ return writeToClipboard(markup);
26
+ }
27
+
28
+ /**
29
+ * Whether the clipboard says it takes an SVG picture. Asked before writing:
30
+ * a write it refuses would spend the click, and the text after it would be
31
+ * refused too in Safari.
32
+ * @returns Whether `ClipboardItem.supports` names the SVG type.
33
+ */
34
+ function takesSvgPicture(): boolean {
35
+ if (typeof ClipboardItem === 'undefined') return false;
36
+ const item = ClipboardItem as { supports?: (type: string) => boolean };
37
+ return (
38
+ typeof item.supports === 'function' && item.supports(SVG_CLIPBOARD_TYPE)
39
+ );
40
+ }
@@ -1,5 +1,6 @@
1
1
  export type { FigureNotice } from './copyFigure.ts';
2
2
  export { copyFigure } from './copyFigure.ts';
3
+ export { copySvg } from './copySvg.ts';
3
4
  export { cssTokenNames, resolveCssTokens } from './cssTokens.ts';
4
5
  export { downloadBlob } from './downloadBlob.ts';
5
6
  export type { DownloadFigureOptions, FigureFormat } from './downloadFigure.ts';
@@ -54,6 +54,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
54
54
  from: 500,
55
55
  to: 600,
56
56
  strength: 'S',
57
+ note: 'Only with bromine in the formula; the mass spectrum shows its 1:1 M+2',
57
58
  },
58
59
  {
59
60
  id: 'c-cl-stretch',
@@ -62,6 +63,16 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
62
63
  from: 600,
63
64
  to: 800,
64
65
  strength: 'S',
66
+ note: 'Only with chlorine in the formula; the mass spectrum shows its 3:1 M+2',
67
+ },
68
+ {
69
+ id: 'alkyne-ch-bend',
70
+ group: 'alkyne',
71
+ vibration: '≡C–H bend',
72
+ from: 610,
73
+ to: 700,
74
+ strength: 'S',
75
+ note: 'Broad, and only beside the sharp ≡C–H stretch near 3300',
65
76
  },
66
77
  {
67
78
  id: 'aromatic-ch-oop',
@@ -97,6 +108,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
97
108
  from: 1000,
98
109
  to: 1400,
99
110
  strength: 'S',
111
+ note: 'Only with fluorine in the formula; a CF₃ gives several strong bands',
100
112
  },
101
113
  {
102
114
  id: 'c-n-stretch',
@@ -105,6 +117,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
105
117
  from: 1080,
106
118
  to: 1360,
107
119
  strength: 'm',
120
+ note: 'Only with nitrogen in the formula, and rarely decisive on its own',
108
121
  },
109
122
  {
110
123
  id: 'nitro-symmetric',
@@ -122,6 +135,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
122
135
  from: 1330,
123
136
  to: 1430,
124
137
  strength: 'm',
138
+ note: 'Only beside the broad O–H stretch above 3200',
125
139
  },
126
140
  {
127
141
  id: 'alkane-ch-bend',
@@ -148,6 +162,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
148
162
  from: 1500,
149
163
  to: 1560,
150
164
  strength: 'S',
165
+ note: 'Never alone: the symmetric partner sits near 1300–1390',
151
166
  },
152
167
  {
153
168
  id: 'nh-bend',
@@ -156,6 +171,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
156
171
  from: 1550,
157
172
  to: 1640,
158
173
  strength: 'm',
174
+ note: 'Only beside an N–H stretch between 3300 and 3500',
159
175
  },
160
176
  {
161
177
  id: 'alkene-cc-stretch',
@@ -218,6 +234,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
218
234
  from: 1770,
219
235
  to: 1815,
220
236
  strength: 'S',
237
+ note: 'The highest single carbonyl, and only with chlorine in the formula',
221
238
  },
222
239
  {
223
240
  id: 'anhydride-co-stretch',
@@ -228,6 +245,15 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
228
245
  strength: 'S',
229
246
  note: 'Two bands, about 60 apart, which is what names it',
230
247
  },
248
+ {
249
+ id: 'allene-stretch',
250
+ group: 'allene',
251
+ vibration: 'C=C=C asymmetric stretch',
252
+ from: 1900,
253
+ to: 2000,
254
+ strength: 'm',
255
+ note: 'Near 1950, where only a cumulated double bond absorbs',
256
+ },
231
257
  {
232
258
  id: 'nitrile-stretch',
233
259
  group: 'nitrile',
@@ -289,6 +315,7 @@ export const BAND_ASSIGNMENTS: readonly BandAssignment[] = [
289
315
  from: 3000,
290
316
  to: 3100,
291
317
  strength: 'm',
318
+ note: 'Above 3000, with the ring pair near 1500 and 1600',
292
319
  },
293
320
  {
294
321
  id: 'alkyne-ch-stretch',
@@ -1,4 +1,5 @@
1
1
  import { Button, Icon } from '@blueprintjs/core';
2
+ import { useState } from 'react';
2
3
 
3
4
  import {
4
5
  panelCellStyle,
@@ -34,10 +35,10 @@ export interface BandRowProps {
34
35
  /**
35
36
  * One band: where it is, how strong, and every vibration it could be.
36
37
  *
37
- * The candidates are written as one line rather than a list, so a strong band
38
- * with four of them stays one row of the table and the wavenumbers keep reading
39
- * down the column. The `title` carries them again with each one's note, which is
40
- * where the reason to prefer one lives.
38
+ * The candidates start as one line rather than a list, so a strong band with
39
+ * four of them stays one row of the table and the wavenumbers keep reading down
40
+ * the column. Its "more" opens the row onto every candidate with its note, which
41
+ * is where the reason to prefer one lives.
41
42
  * @param props - Component props.
42
43
  * @returns The row.
43
44
  */
@@ -47,7 +48,7 @@ export function BandRow(props: BandRowProps) {
47
48
 
48
49
  return (
49
50
  <tr
50
- style={lit ? litRowStyle : undefined}
51
+ style={lit ? litRowStyle : rowStyle}
51
52
  onPointerEnter={() => onHighlight?.(band)}
52
53
  onPointerLeave={() => onHighlight?.(null)}
53
54
  >
@@ -80,35 +81,63 @@ interface CandidatesCellProps {
80
81
  }
81
82
 
82
83
  /**
83
- * What a band could be: the narrowest candidate, and how many more there are.
84
+ * What a band could be: the narrowest candidate and how many more there are,
85
+ * opened on a click into every candidate with the note that tells it apart.
84
86
  * @param props - Component props.
85
87
  * @returns The cell.
86
88
  */
87
89
  function CandidatesCell(props: CandidatesCellProps) {
88
90
  const { candidates } = props.entry;
89
- const detail = candidates
90
- .map((candidate) =>
91
- candidate.note === undefined
92
- ? `${candidate.group} ${candidate.vibration}`
93
- : `${candidate.group} ${candidate.vibration} — ${candidate.note}`,
94
- )
95
- .join('\n');
91
+ const [open, setOpen] = useState(false);
96
92
 
97
- return (
98
- <td style={panelCellStyle} title={detail}>
99
- {candidates.length === 0 ? (
93
+ if (candidates.length === 0) {
94
+ return (
95
+ <td style={panelCellStyle}>
100
96
  <span style={{ opacity: 0.5 }}>—</span>
97
+ </td>
98
+ );
99
+ }
100
+
101
+ const [first] = candidates;
102
+ const others = candidates.length - 1;
103
+
104
+ return (
105
+ <td style={candidatesCellStyle}>
106
+ {open ? (
107
+ <ul style={candidateListStyle}>
108
+ {candidates.map((candidate) => (
109
+ <li key={candidate.id}>
110
+ {candidate.group} {candidate.vibration}
111
+ {candidate.note === undefined ? null : (
112
+ <div style={candidateNoteStyle}>{candidate.note}</div>
113
+ )}
114
+ </li>
115
+ ))}
116
+ </ul>
101
117
  ) : (
102
- <span style={{ whiteSpace: 'normal' }}>
103
- {candidates[0]?.group} {candidates[0]?.vibration}
104
- {candidates.length > 1 ? (
105
- <span style={{ opacity: 0.6 }}>
106
- {' '}
107
- <Icon icon="more" size={10} /> {candidates.length - 1} more
108
- </span>
109
- ) : null}
110
- </span>
118
+ <>
119
+ {first?.group} {first?.vibration}
120
+ </>
111
121
  )}
122
+ {others > 0 ? (
123
+ <>
124
+ {open ? null : ' '}
125
+ <button
126
+ type="button"
127
+ style={moreButtonStyle}
128
+ aria-expanded={open}
129
+ onClick={() => setOpen(!open)}
130
+ >
131
+ {open ? (
132
+ 'less'
133
+ ) : (
134
+ <>
135
+ <Icon icon="more" size={10} /> {others} more
136
+ </>
137
+ )}
138
+ </button>
139
+ </>
140
+ ) : null}
112
141
  </td>
113
142
  );
114
143
  }
@@ -120,6 +149,35 @@ const STRENGTH_TITLES = {
120
149
  S: 'strong, relative to this spectrum',
121
150
  } as const;
122
151
 
123
- const litRowStyle = { background: '#fff3e0' } as const;
152
+ // Top, so an opened row keeps its wavenumber on the line of its first candidate.
153
+ const rowStyle = { verticalAlign: 'top' } as const;
154
+
155
+ const litRowStyle = { ...rowStyle, background: '#fff3e0' } as const;
124
156
 
125
157
  const removeCellStyle = { ...panelCellStyle, padding: 0, width: 1 } as const;
158
+
159
+ const candidatesCellStyle = {
160
+ ...panelCellStyle,
161
+ whiteSpace: 'normal',
162
+ } as const;
163
+
164
+ const candidateListStyle = {
165
+ display: 'flex',
166
+ flexDirection: 'column',
167
+ gap: 4,
168
+ margin: 0,
169
+ padding: 0,
170
+ listStyle: 'none',
171
+ } as const;
172
+
173
+ const candidateNoteStyle = { fontSize: '0.9em', opacity: 0.6 } as const;
174
+
175
+ const moreButtonStyle = {
176
+ padding: 0,
177
+ border: 0,
178
+ background: 'none',
179
+ color: 'inherit',
180
+ font: 'inherit',
181
+ opacity: 0.6,
182
+ cursor: 'pointer',
183
+ } as const;
@@ -587,6 +587,8 @@
587
587
  "structure.export.saveSvg": "Als SVG speichern",
588
588
  "structure.export.savePng": "Als PNG speichern",
589
589
  "structure.export.copyPng": "PNG kopieren",
590
+ "structure.export.copySvg": "SVG kopieren",
591
+ "structure.export.svgCopied": "In die Zwischenablage kopiert: SVG.",
590
592
  "structure.export.smiles": "SMILES",
591
593
  "structure.export.smilesHelp": "Die Struktur in einer Zeile, Stereochemie eingeschlossen. Jedes Programm liest sie.",
592
594
  "structure.export.molfileV3000": "Molfile V3000",
@@ -531,6 +531,8 @@
531
531
  "structure.export.saveSvg": "Save SVG",
532
532
  "structure.export.savePng": "Save PNG",
533
533
  "structure.export.copyPng": "Copy PNG",
534
+ "structure.export.copySvg": "Copy SVG",
535
+ "structure.export.svgCopied": "Copied the SVG to the clipboard.",
534
536
  "structure.export.smiles": "SMILES",
535
537
  "structure.export.smilesHelp": "The structure on one line, stereochemistry included. Every tool reads it.",
536
538
  "structure.export.molfileV3000": "Molfile V3000",
@@ -587,6 +587,8 @@
587
587
  "structure.export.saveSvg": "Guardar en SVG",
588
588
  "structure.export.savePng": "Guardar en PNG",
589
589
  "structure.export.copyPng": "Copiar el PNG",
590
+ "structure.export.copySvg": "Copiar el SVG",
591
+ "structure.export.svgCopied": "Copiado al portapapeles: SVG.",
590
592
  "structure.export.smiles": "SMILES",
591
593
  "structure.export.smilesHelp": "La estructura en una línea, con su estereoquímica. Todas las herramientas la leen.",
592
594
  "structure.export.molfileV3000": "Molfile V3000",
@@ -587,6 +587,8 @@
587
587
  "structure.export.saveSvg": "Enregistrer en SVG",
588
588
  "structure.export.savePng": "Enregistrer en PNG",
589
589
  "structure.export.copyPng": "Copier le PNG",
590
+ "structure.export.copySvg": "Copier le SVG",
591
+ "structure.export.svgCopied": "Copié dans le presse-papiers : SVG.",
590
592
  "structure.export.smiles": "SMILES",
591
593
  "structure.export.smilesHelp": "La structure sur une ligne, stéréochimie comprise. Tous les logiciels le lisent.",
592
594
  "structure.export.molfileV3000": "Molfile V3000",
@@ -531,6 +531,8 @@
531
531
  "structure.export.saveSvg": "Salva in SVG",
532
532
  "structure.export.savePng": "Salva in PNG",
533
533
  "structure.export.copyPng": "Copia il PNG",
534
+ "structure.export.copySvg": "Copia l’SVG",
535
+ "structure.export.svgCopied": "Copiato negli appunti: SVG.",
534
536
  "structure.export.smiles": "SMILES",
535
537
  "structure.export.smilesHelp": "La struttura su una riga, stereochimica compresa. Ogni strumento la legge.",
536
538
  "structure.export.molfileV3000": "Molfile V3000",
@@ -0,0 +1,76 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+
3
+ import { OverlayLayer } from '../../overlay/ui/OverlayLayer.tsx';
4
+ import { structureSource } from '../core/structureSource.ts';
5
+
6
+ import type { StructureProps } from './Structure.tsx';
7
+ import { Structure } from './Structure.tsx';
8
+ import { StructureExportButton } from './StructureExportButton.tsx';
9
+
10
+ /** What {@link CardStructure} draws and hands over. */
11
+ export interface CardStructureProps extends StructureProps {
12
+ /**
13
+ * Base name of every file saved from the export, extension excluded.
14
+ * @default 'structure'
15
+ */
16
+ name?: string;
17
+ /**
18
+ * Whether the structure is a query fragment, such as a toxicophore, whose
19
+ * idCode is not a molecule's.
20
+ * @default false
21
+ */
22
+ fragment?: boolean;
23
+ /**
24
+ * What the glyph is called, for the pointer and for a screen reader.
25
+ * @default the chrome's own line, `Copy or save the structure: SMILES,
26
+ * molfile`
27
+ */
28
+ label?: string;
29
+ /**
30
+ * The drawing, when the site draws the structure itself — its own
31
+ * highlights, its own numbering. The export still reads the notations.
32
+ * @default a `Structure` drawn from the same notations, at the same size
33
+ */
34
+ children?: ReactNode;
35
+ }
36
+
37
+ /**
38
+ * A structure of a card, with the glyph that hands it over: the export
39
+ * dialog, which copies it as an SVG or a PNG, a SMILES or a molfile, V2000 or
40
+ * V3000. Every structure an answer draws carries it, alone or in a grid, so
41
+ * none has to be redrawn to be reused.
42
+ *
43
+ * The glyph stands beside the top right corner, faded until the structure is
44
+ * pointed at, as the glyphs of `CardFigure` (`react-cheminfo/card`) do;
45
+ * `styles/card.css` draws both. A structure with nothing to read draws its
46
+ * placeholder alone.
47
+ * @param props - The structure, its size, and the name of its files.
48
+ * @returns The structure and its glyph.
49
+ */
50
+ export function CardStructure(props: CardStructureProps): ReactElement {
51
+ const { name, fragment, label, children, ...structure } = props;
52
+ const { idCode, coordinates, molfile, smiles } = structure;
53
+ const drawing = children ?? <Structure {...structure} />;
54
+ const source = structureSource({ idCode, coordinates, molfile, smiles });
55
+ if (source.kind === 'empty') return <>{drawing}</>;
56
+
57
+ return (
58
+ <div className="card-figure card-structure">
59
+ <div className="card-figure__drawing">{drawing}</div>
60
+ {/* Compact: the glyphs of a figure shown in somebody else's page. */}
61
+ <OverlayLayer density="compact" awake>
62
+ <div className="card-figure__tools" data-testid="figure-tools">
63
+ <StructureExportButton
64
+ idCode={idCode}
65
+ coordinates={coordinates}
66
+ molfile={molfile}
67
+ smiles={smiles}
68
+ name={name}
69
+ fragment={fragment}
70
+ label={label}
71
+ />
72
+ </div>
73
+ </OverlayLayer>
74
+ </div>
75
+ );
76
+ }
@@ -4,6 +4,7 @@ import type { CSSProperties, ReactElement } from 'react';
4
4
  import { useMemo, useState } from 'react';
5
5
 
6
6
  import { copyFigure } from '../../download/core/copyFigure.ts';
7
+ import { copySvg } from '../../download/core/copySvg.ts';
7
8
  import { downloadBlob } from '../../download/core/downloadBlob.ts';
8
9
  import { downloadText } from '../../download/core/downloadText.ts';
9
10
  import { figurePng } from '../../download/core/figurePng.ts';
@@ -37,7 +38,7 @@ const DEFAULT_SCALE = 2;
37
38
 
38
39
  /**
39
40
  * The structure as a picture: what it looks like, how big it leaves, and the
40
- * three ways it leaves.
41
+ * four ways it leaves.
41
42
  *
42
43
  * What is shown is the file: the preview is the very SVG the save button
43
44
  * writes, laid out smaller, so nobody saves a picture they have not seen. The
@@ -136,6 +137,18 @@ export function StructurePicturePane(
136
137
  })
137
138
  }
138
139
  />
140
+ <Button
141
+ icon="clipboard"
142
+ size="small"
143
+ text={t('structure.export.copySvg')}
144
+ onClick={() =>
145
+ void run(async () =>
146
+ (await copySvg(picture.markup))
147
+ ? t('structure.export.svgCopied')
148
+ : t('clipboard.failed'),
149
+ )
150
+ }
151
+ />
139
152
  <Button
140
153
  icon="clipboard"
141
154
  size="small"
@@ -33,6 +33,8 @@ export type { StructureExportDialogProps } from './StructureExportDialog.tsx';
33
33
  export { StructureExportDialog } from './StructureExportDialog.tsx';
34
34
  export type { StructureExportButtonProps } from './StructureExportButton.tsx';
35
35
  export { StructureExportButton } from './StructureExportButton.tsx';
36
+ export type { CardStructureProps } from './CardStructure.tsx';
37
+ export { CardStructure } from './CardStructure.tsx';
36
38
  export type { StructureExportPanelProps } from './StructureExportPanel.tsx';
37
39
  export type { ToolbarFloorOptions } from './useToolbarFloor.ts';
38
40
  export { useToolbarFloor } from './useToolbarFloor.ts';
package/styles/chrome.css CHANGED
@@ -850,9 +850,9 @@ a.source-badge:hover {
850
850
  gap: 8px;
851
851
  }
852
852
 
853
- /* Two columns, so the three buttons are one size: a row of buttons whose
854
- widths are set by the length of their labels reads as three unrelated
855
- controls. */
853
+ /* Two columns, so the buttons are one size — the saves over the copies, SVG
854
+ over SVG: a row of buttons whose widths are set by the length of their
855
+ labels reads as unrelated controls. */
856
856
  .structure-export__actions {
857
857
  display: grid;
858
858
  margin-top: 2px;