react-cheminfo 0.17.0 → 0.18.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 (80) hide show
  1. package/lib/structure/core/editorGuide.d.ts +69 -0
  2. package/lib/structure/core/editorGuide.d.ts.map +1 -0
  3. package/lib/structure/core/editorGuide.js +162 -0
  4. package/lib/structure/core/editorGuide.js.map +1 -0
  5. package/lib/structure/core/editorToolbar.d.ts +45 -0
  6. package/lib/structure/core/editorToolbar.d.ts.map +1 -0
  7. package/lib/structure/core/editorToolbar.js +132 -0
  8. package/lib/structure/core/editorToolbar.js.map +1 -0
  9. package/lib/structure/core/editorToolbarGeometry.d.ts +36 -0
  10. package/lib/structure/core/editorToolbarGeometry.d.ts.map +1 -0
  11. package/lib/structure/core/editorToolbarGeometry.js +61 -0
  12. package/lib/structure/core/editorToolbarGeometry.js.map +1 -0
  13. package/lib/structure/core/index.d.ts +6 -0
  14. package/lib/structure/core/index.d.ts.map +1 -1
  15. package/lib/structure/core/index.js +3 -0
  16. package/lib/structure/core/index.js.map +1 -1
  17. package/lib/structure/core/pendingCall.d.ts +21 -0
  18. package/lib/structure/core/pendingCall.d.ts.map +1 -0
  19. package/lib/structure/core/pendingCall.js +37 -0
  20. package/lib/structure/core/pendingCall.js.map +1 -0
  21. package/lib/structure/ui/EditorCanvas.d.ts +1 -16
  22. package/lib/structure/ui/EditorCanvas.d.ts.map +1 -1
  23. package/lib/structure/ui/EditorCanvas.js +5 -18
  24. package/lib/structure/ui/EditorCanvas.js.map +1 -1
  25. package/lib/structure/ui/EditorHelpButton.d.ts +22 -0
  26. package/lib/structure/ui/EditorHelpButton.d.ts.map +1 -0
  27. package/lib/structure/ui/EditorHelpButton.js +38 -0
  28. package/lib/structure/ui/EditorHelpButton.js.map +1 -0
  29. package/lib/structure/ui/KeyCaps.d.ts +12 -0
  30. package/lib/structure/ui/KeyCaps.d.ts.map +1 -0
  31. package/lib/structure/ui/KeyCaps.js +29 -0
  32. package/lib/structure/ui/KeyCaps.js.map +1 -0
  33. package/lib/structure/ui/StructureEditor.d.ts +9 -2
  34. package/lib/structure/ui/StructureEditor.d.ts.map +1 -1
  35. package/lib/structure/ui/StructureEditor.js +17 -24
  36. package/lib/structure/ui/StructureEditor.js.map +1 -1
  37. package/lib/structure/ui/StructureEditorHelp.d.ts +18 -0
  38. package/lib/structure/ui/StructureEditorHelp.d.ts.map +1 -0
  39. package/lib/structure/ui/StructureEditorHelp.js +68 -0
  40. package/lib/structure/ui/StructureEditorHelp.js.map +1 -0
  41. package/lib/structure/ui/ToolbarTooltip.d.ts +20 -0
  42. package/lib/structure/ui/ToolbarTooltip.d.ts.map +1 -0
  43. package/lib/structure/ui/ToolbarTooltip.js +49 -0
  44. package/lib/structure/ui/ToolbarTooltip.js.map +1 -0
  45. package/lib/structure/ui/editorChange.d.ts +52 -0
  46. package/lib/structure/ui/editorChange.d.ts.map +1 -0
  47. package/lib/structure/ui/editorChange.js +34 -0
  48. package/lib/structure/ui/editorChange.js.map +1 -0
  49. package/lib/structure/ui/index.d.ts +3 -1
  50. package/lib/structure/ui/index.d.ts.map +1 -1
  51. package/lib/structure/ui/index.js +1 -0
  52. package/lib/structure/ui/index.js.map +1 -1
  53. package/lib/structure/ui/useToolbarFloor.d.ts.map +1 -1
  54. package/lib/structure/ui/useToolbarFloor.js +6 -45
  55. package/lib/structure/ui/useToolbarFloor.js.map +1 -1
  56. package/lib/structure/ui/useToolbarHover.d.ts +28 -0
  57. package/lib/structure/ui/useToolbarHover.d.ts.map +1 -0
  58. package/lib/structure/ui/useToolbarHover.js +96 -0
  59. package/lib/structure/ui/useToolbarHover.js.map +1 -0
  60. package/lib/structure/ui/watchToolbar.d.ts +17 -0
  61. package/lib/structure/ui/watchToolbar.d.ts.map +1 -0
  62. package/lib/structure/ui/watchToolbar.js +56 -0
  63. package/lib/structure/ui/watchToolbar.js.map +1 -0
  64. package/package.json +1 -1
  65. package/src/structure/core/editorGuide.ts +221 -0
  66. package/src/structure/core/editorToolbar.ts +175 -0
  67. package/src/structure/core/editorToolbarGeometry.ts +80 -0
  68. package/src/structure/core/index.ts +29 -0
  69. package/src/structure/core/pendingCall.ts +59 -0
  70. package/src/structure/ui/EditorCanvas.tsx +9 -35
  71. package/src/structure/ui/EditorHelpButton.tsx +70 -0
  72. package/src/structure/ui/KeyCaps.tsx +40 -0
  73. package/src/structure/ui/StructureEditor.tsx +39 -21
  74. package/src/structure/ui/StructureEditorHelp.tsx +136 -0
  75. package/src/structure/ui/ToolbarTooltip.tsx +95 -0
  76. package/src/structure/ui/editorChange.ts +81 -0
  77. package/src/structure/ui/index.ts +5 -1
  78. package/src/structure/ui/useToolbarFloor.ts +7 -46
  79. package/src/structure/ui/useToolbarHover.ts +134 -0
  80. package/src/structure/ui/watchToolbar.ts +59 -0
@@ -9,11 +9,13 @@
9
9
  * optional peers are not installed — which is what a site wanting only the
10
10
  * Tools menu does. Type-only re-exports are erased and so are safe.
11
11
  */
12
- export type { StructureEditorChange, StructureEditorMode, } from './EditorCanvas.tsx';
12
+ export type { StructureEditorChange, StructureEditorMode, StructureEditorMoleculeChange, StructureEditorReactionChange, } from './editorChange.ts';
13
13
  export type { StructureLabels, StructureProps } from './Structure.tsx';
14
14
  export { Structure } from './Structure.tsx';
15
15
  export type { StructureEditorProps } from './StructureEditor.tsx';
16
16
  export { StructureEditor } from './StructureEditor.tsx';
17
+ export type { StructureEditorHelpProps } from './StructureEditorHelp.tsx';
18
+ export { StructureEditorHelp } from './StructureEditorHelp.tsx';
17
19
  export type { ToolbarFloorOptions } from './useToolbarFloor.ts';
18
20
  export { useToolbarFloor } from './useToolbarFloor.ts';
19
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,YAAY,EACV,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,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,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;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,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
@@ -11,5 +11,6 @@
11
11
  */
12
12
  export { Structure } from "./Structure.js";
13
13
  export { StructureEditor } from "./StructureEditor.js";
14
+ export { StructureEditorHelp } from "./StructureEditorHelp.js";
14
15
  export { useToolbarFloor } from "./useToolbarFloor.js";
15
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAiB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAuB,CAAC;AAExD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/structure/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAiB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAuB,CAAC;AAExD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA2B,CAAC;AAEhE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useToolbarFloor.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/useToolbarFloor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,OAAO,GAAE,mBAAwB,GAChC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAiDlC"}
1
+ {"version":3,"file":"useToolbarFloor.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/useToolbarFloor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAOvC,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,OAAO,GAAE,mBAAwB,GAChC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAyBlC"}
@@ -1,5 +1,6 @@
1
1
  import { useLayoutEffect, useRef } from 'react';
2
2
  import { toolbarFloorHeight } from "../core/toolbarFloor.js";
3
+ import { watchToolbar } from "./watchToolbar.js";
3
4
  /**
4
5
  * Give an editor's container a minimum height that shows the whole toolbar.
5
6
  *
@@ -16,18 +17,11 @@ export function useToolbarFloor(options = {}) {
16
17
  const container = containerRef.current;
17
18
  if (container === null)
18
19
  return;
19
- let toolbar = null;
20
- let size = null;
21
- const attach = () => {
22
- const found = findToolbar(container);
23
- if (found === null)
24
- return;
25
- size?.disconnect();
26
- toolbar = found;
27
- size = new ResizeObserver(() => {
20
+ return watchToolbar(container, (toolbar) => {
21
+ const size = new ResizeObserver(() => {
28
22
  // offsetHeight, because a dialog opens under a scaling transform and a
29
23
  // measured rectangle would be that of the half drawn toolbar.
30
- const height = found.offsetHeight;
24
+ const height = toolbar.offsetHeight;
31
25
  // Zero is a toolbar being torn down or not yet laid out, never a
32
26
  // toolbar that needs no room: writing the fallback for it would undo a
33
27
  // correct floor and leave the palette cut off.
@@ -35,43 +29,10 @@ export function useToolbarFloor(options = {}) {
35
29
  return;
36
30
  container.style.minHeight = `${toolbarFloorHeight(height, minHeight)}px`;
37
31
  });
38
- size.observe(found);
39
- };
40
- // The editor is imported lazily and builds itself asynchronously, so it is
41
- // almost never there on the first look, and how long it takes is a cold
42
- // module graph and a network away from anything this code can predict. So
43
- // the arrival is watched for rather than waited out: a deadline that
44
- // expires leaves the toolbar cut off for the life of the page, with
45
- // nothing on screen to say why.
46
- const arrivals = new MutationObserver(() => {
47
- if (toolbar?.isConnected === true)
48
- return;
49
- attach();
32
+ size.observe(toolbar);
33
+ return () => size.disconnect();
50
34
  });
51
- arrivals.observe(container, { childList: true, subtree: true });
52
- attach();
53
- return () => {
54
- arrivals.disconnect();
55
- size?.disconnect();
56
- };
57
35
  }, [minHeight, revision]);
58
36
  return containerRef;
59
37
  }
60
- /**
61
- * Find the toolbar canvas of the editor.
62
- *
63
- * The editor builds itself inside a shadow root and puts the toolbar there as
64
- * a direct child, the drawing canvas being nested deeper.
65
- * @param container - The element wrapping the editor.
66
- * @returns The toolbar canvas, or null while the editor has not drawn one.
67
- */
68
- function findToolbar(container) {
69
- for (const element of container.querySelectorAll('*')) {
70
- for (const child of element.shadowRoot?.children ?? []) {
71
- if (child instanceof HTMLCanvasElement)
72
- return child;
73
- }
74
- }
75
- return null;
76
- }
77
38
  //# sourceMappingURL=useToolbarFloor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useToolbarFloor.js","sourceRoot":"","sources":["../../../src/structure/ui/useToolbarFloor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAkB7D;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,UAA+B,EAAE;IAEjC,MAAM,EAAE,SAAS,GAAG,GAAG,EAAE,QAAQ,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAClD,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAElD,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;QACvC,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO;QAE/B,IAAI,OAAO,GAA6B,IAAI,CAAC;QAC7C,IAAI,IAAI,GAA0B,IAAI,CAAC;QAEvC,MAAM,MAAM,GAAG,GAAS,EAAE;YACxB,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;YACrC,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO;YAC3B,IAAI,EAAE,UAAU,EAAE,CAAC;YACnB,OAAO,GAAG,KAAK,CAAC;YAChB,IAAI,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE;gBAC7B,uEAAuE;gBACvE,8DAA8D;gBAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC;gBAClC,iEAAiE;gBACjE,uEAAuE;gBACvE,+CAA+C;gBAC/C,IAAI,MAAM,KAAK,CAAC;oBAAE,OAAO;gBACzB,SAAS,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC;YAC3E,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC;QAEF,2EAA2E;QAC3E,wEAAwE;QACxE,0EAA0E;QAC1E,qEAAqE;QACrE,oEAAoE;QACpE,gCAAgC;QAChC,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,GAAG,EAAE;YACzC,IAAI,OAAO,EAAE,WAAW,KAAK,IAAI;gBAAE,OAAO;YAC1C,MAAM,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,EAAE,CAAC;QAET,OAAO,GAAG,EAAE;YACV,QAAQ,CAAC,UAAU,EAAE,CAAC;YACtB,IAAI,EAAE,UAAU,EAAE,CAAC;QACrB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE1B,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,WAAW,CAAC,SAAsB;IACzC,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,UAAU,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC;YACvD,IAAI,KAAK,YAAY,iBAAiB;gBAAE,OAAO,KAAK,CAAC;QACvD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"useToolbarFloor.js","sourceRoot":"","sources":["../../../src/structure/ui/useToolbarFloor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAkBjD;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,UAA+B,EAAE;IAEjC,MAAM,EAAE,SAAS,GAAG,GAAG,EAAE,QAAQ,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAClD,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAElD,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;QACvC,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO;QAE/B,OAAO,YAAY,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACzC,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE;gBACnC,uEAAuE;gBACvE,8DAA8D;gBAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;gBACpC,iEAAiE;gBACjE,uEAAuE;gBACvE,+CAA+C;gBAC/C,IAAI,MAAM,KAAK,CAAC;oBAAE,OAAO;gBACzB,SAAS,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC;YAC3E,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE1B,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { RefObject } from 'react';
2
+ import type { EditorToolbarButtonBox } from '../core/editorToolbarGeometry.ts';
3
+ /** The toolbar button the pointer rests on, or last rested on. */
4
+ export interface ToolbarHover {
5
+ /** The button's index in `EDITOR_TOOLBAR_BUTTONS`. */
6
+ button: number;
7
+ /** Where the button is drawn, relative to the container's padding box. */
8
+ box: EditorToolbarButtonBox;
9
+ /**
10
+ * Whether its tooltip is showing. The last button is kept after the pointer
11
+ * leaves, so a closing tooltip fades out with its text rather than empty.
12
+ */
13
+ open: boolean;
14
+ }
15
+ /**
16
+ * Follow the pointer over the editor's toolbar.
17
+ *
18
+ * The first button waits for the pointer to rest, so crossing the toolbar on
19
+ * the way to the drawing opens nothing; once one is showing, the next follows
20
+ * at once. Pressing a button hides its tooltip until the pointer moves on,
21
+ * since whoever pressed it no longer needs telling. Touch is ignored: a finger
22
+ * has no hover, and a tooltip opened by a tap would cover the canvas.
23
+ * @param containerRef - The element wrapping the editor, positioned so the
24
+ * returned box can be laid over the button.
25
+ * @returns The button under the pointer, or null before any has been hovered.
26
+ */
27
+ export declare function useToolbarHover(containerRef: RefObject<HTMLElement | null>): ToolbarHover | null;
28
+ //# sourceMappingURL=useToolbarHover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToolbarHover.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/useToolbarHover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAQ/E,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,GAAG,EAAE,sBAAsB,CAAC;IAC5B;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAC1C,YAAY,GAAG,IAAI,CA4ErB"}
@@ -0,0 +1,96 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { editorToolbarButtonAt, editorToolbarButtonBox, } from "../core/editorToolbarGeometry.js";
3
+ import { watchToolbar } from "./watchToolbar.js";
4
+ /**
5
+ * Follow the pointer over the editor's toolbar.
6
+ *
7
+ * The first button waits for the pointer to rest, so crossing the toolbar on
8
+ * the way to the drawing opens nothing; once one is showing, the next follows
9
+ * at once. Pressing a button hides its tooltip until the pointer moves on,
10
+ * since whoever pressed it no longer needs telling. Touch is ignored: a finger
11
+ * has no hover, and a tooltip opened by a tap would cover the canvas.
12
+ * @param containerRef - The element wrapping the editor, positioned so the
13
+ * returned box can be laid over the button.
14
+ * @returns The button under the pointer, or null before any has been hovered.
15
+ */
16
+ export function useToolbarHover(containerRef) {
17
+ const [hover, setHover] = useState(null);
18
+ useEffect(() => {
19
+ const container = containerRef.current;
20
+ if (container === null)
21
+ return;
22
+ let timer;
23
+ let current = -1;
24
+ let pressed = -1;
25
+ let open = false;
26
+ const show = (toolbar, button) => {
27
+ open = true;
28
+ setHover({ button, box: boxIn(container, toolbar, button), open });
29
+ };
30
+ const hide = () => {
31
+ clearTimeout(timer);
32
+ if (!open)
33
+ return;
34
+ open = false;
35
+ setHover((previous) => previous === null ? null : { ...previous, open: false });
36
+ };
37
+ const onLeave = () => {
38
+ current = -1;
39
+ pressed = -1;
40
+ hide();
41
+ };
42
+ const onDown = () => {
43
+ pressed = current;
44
+ hide();
45
+ };
46
+ const stop = watchToolbar(container, (toolbar) => {
47
+ const onMove = (event) => {
48
+ if (event.pointerType === 'touch')
49
+ return;
50
+ const button = editorToolbarButtonAt(event.offsetX, event.offsetY, toolbar.offsetHeight);
51
+ if (button === current)
52
+ return;
53
+ current = button;
54
+ if (button !== pressed)
55
+ pressed = -1;
56
+ if (button === -1 || button === pressed) {
57
+ hide();
58
+ return;
59
+ }
60
+ clearTimeout(timer);
61
+ if (open) {
62
+ show(toolbar, button);
63
+ return;
64
+ }
65
+ timer = setTimeout(() => show(toolbar, button), OPEN_DELAY);
66
+ };
67
+ toolbar.addEventListener('pointermove', onMove);
68
+ toolbar.addEventListener('pointerleave', onLeave);
69
+ toolbar.addEventListener('pointerdown', onDown);
70
+ return () => {
71
+ toolbar.removeEventListener('pointermove', onMove);
72
+ toolbar.removeEventListener('pointerleave', onLeave);
73
+ toolbar.removeEventListener('pointerdown', onDown);
74
+ onLeave();
75
+ };
76
+ });
77
+ return () => {
78
+ stop();
79
+ clearTimeout(timer);
80
+ };
81
+ }, [containerRef]);
82
+ return hover;
83
+ }
84
+ // Long enough that sweeping the pointer across the toolbar opens nothing.
85
+ const OPEN_DELAY = 250;
86
+ function boxIn(container, toolbar, button) {
87
+ const box = editorToolbarButtonBox(button, toolbar.offsetHeight);
88
+ const toolbarRect = toolbar.getBoundingClientRect();
89
+ const containerRect = container.getBoundingClientRect();
90
+ return {
91
+ ...box,
92
+ left: toolbarRect.left - containerRect.left - container.clientLeft + box.left,
93
+ top: toolbarRect.top - containerRect.top - container.clientTop + box.top,
94
+ };
95
+ }
96
+ //# sourceMappingURL=useToolbarHover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToolbarHover.js","sourceRoot":"","sources":["../../../src/structure/ui/useToolbarHover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Run something against the editor's toolbar canvas as soon as it exists, and
3
+ * again against each new one the editor builds.
4
+ *
5
+ * The editor is imported lazily and builds itself asynchronously, so the
6
+ * toolbar is almost never there on the first look, and how long it takes is a
7
+ * cold module graph and a network away from anything this code can predict.
8
+ * So its arrival is watched for rather than waited out: a deadline that
9
+ * expires leaves whatever depends on the toolbar missing for the life of the
10
+ * page, with nothing on screen to say why.
11
+ * @param container - The element wrapping the editor.
12
+ * @param onToolbar - Called with each toolbar found; what it returns is called
13
+ * when that toolbar is replaced or the watch ends.
14
+ * @returns A function ending the watch.
15
+ */
16
+ export declare function watchToolbar(container: HTMLElement, onToolbar: (toolbar: HTMLCanvasElement) => () => void): () => void;
17
+ //# sourceMappingURL=watchToolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watchToolbar.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/watchToolbar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,WAAW,EACtB,SAAS,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,MAAM,IAAI,GACpD,MAAM,IAAI,CAuBZ"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Run something against the editor's toolbar canvas as soon as it exists, and
3
+ * again against each new one the editor builds.
4
+ *
5
+ * The editor is imported lazily and builds itself asynchronously, so the
6
+ * toolbar is almost never there on the first look, and how long it takes is a
7
+ * cold module graph and a network away from anything this code can predict.
8
+ * So its arrival is watched for rather than waited out: a deadline that
9
+ * expires leaves whatever depends on the toolbar missing for the life of the
10
+ * page, with nothing on screen to say why.
11
+ * @param container - The element wrapping the editor.
12
+ * @param onToolbar - Called with each toolbar found; what it returns is called
13
+ * when that toolbar is replaced or the watch ends.
14
+ * @returns A function ending the watch.
15
+ */
16
+ export function watchToolbar(container, onToolbar) {
17
+ let toolbar = null;
18
+ let release = null;
19
+ const attach = () => {
20
+ const found = findToolbar(container);
21
+ if (found === null)
22
+ return;
23
+ release?.();
24
+ toolbar = found;
25
+ release = onToolbar(found);
26
+ };
27
+ const arrivals = new MutationObserver(() => {
28
+ if (toolbar?.isConnected === true)
29
+ return;
30
+ attach();
31
+ });
32
+ arrivals.observe(container, { childList: true, subtree: true });
33
+ attach();
34
+ return () => {
35
+ arrivals.disconnect();
36
+ release?.();
37
+ };
38
+ }
39
+ /**
40
+ * Find the toolbar canvas of the editor.
41
+ *
42
+ * The editor builds itself inside a shadow root and puts the toolbar there as
43
+ * a direct child, the drawing canvas being nested deeper.
44
+ * @param container - The element wrapping the editor.
45
+ * @returns The toolbar canvas, or null while the editor has not drawn one.
46
+ */
47
+ function findToolbar(container) {
48
+ for (const element of container.querySelectorAll('*')) {
49
+ for (const child of element.shadowRoot?.children ?? []) {
50
+ if (child instanceof HTMLCanvasElement)
51
+ return child;
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+ //# sourceMappingURL=watchToolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watchToolbar.js","sourceRoot":"","sources":["../../../src/structure/ui/watchToolbar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAsB,EACtB,SAAqD;IAErD,IAAI,OAAO,GAA6B,IAAI,CAAC;IAC7C,IAAI,OAAO,GAAwB,IAAI,CAAC;IAExC,MAAM,MAAM,GAAG,GAAS,EAAE;QACxB,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO;QAC3B,OAAO,EAAE,EAAE,CAAC;QACZ,OAAO,GAAG,KAAK,CAAC;QAChB,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,GAAG,EAAE;QACzC,IAAI,OAAO,EAAE,WAAW,KAAK,IAAI;YAAE,OAAO;QAC1C,MAAM,EAAE,CAAC;IACX,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,MAAM,EAAE,CAAC;IAET,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,UAAU,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,WAAW,CAAC,SAAsB;IACzC,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,UAAU,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC;YACvD,IAAI,KAAK,YAAY,iBAAiB;gBAAE,OAAO,KAAK,CAAC;QACvD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-cheminfo",
3
- "version": "0.17.0",
3
+ "version": "0.18.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,221 @@
1
+ /**
2
+ * What the keyboard and the mouse do in the openchemlib canvas editor, beyond
3
+ * what its toolbar shows: the keys that change the atom or the bond under the
4
+ * pointer, the selection gestures and the stereo conventions.
5
+ */
6
+
7
+ /** A key, drawn as a key cap. `Mod` is ⌘ on a Mac and Ctrl elsewhere. */
8
+ export interface EditorGuideKey {
9
+ key: string;
10
+ }
11
+
12
+ /** Which editors a gesture applies to. */
13
+ export type EditorGuideScope = 'fragment' | 'molecule' | 'reaction';
14
+
15
+ /** One gesture and what it does. */
16
+ export interface EditorGesture {
17
+ /** Keys, and plain text for the mouse part; unique within one gesture. */
18
+ input: ReadonlyArray<string | EditorGuideKey>;
19
+ action: string;
20
+ /**
21
+ * The only editor the gesture works in.
22
+ * @default undefined — every editor
23
+ */
24
+ only?: EditorGuideScope;
25
+ }
26
+
27
+ /** Gestures that share a context, such as the pointer resting on an atom. */
28
+ export interface EditorGuideSection {
29
+ title: string;
30
+ gestures: readonly EditorGesture[];
31
+ /**
32
+ * A sentence read after the gestures.
33
+ * @default undefined
34
+ */
35
+ note?: string;
36
+ }
37
+
38
+ /** A page of the full documentation. */
39
+ export interface EditorGuideLink {
40
+ title: string;
41
+ url: string;
42
+ }
43
+
44
+ /** The sections, in the order the guide lists them. */
45
+ export const STRUCTURE_EDITOR_GUIDE: readonly EditorGuideSection[] = [
46
+ {
47
+ title: 'Pointer on an atom',
48
+ gestures: [
49
+ { input: ['Type', { key: 'n' }, { key: 'a' }], action: 'Make it Na' },
50
+ {
51
+ input: ['Type', { key: 'p' }, { key: 'h' }],
52
+ action: 'Attach a phenyl; Me, Et, Boc, TMS… work too',
53
+ },
54
+ {
55
+ input: [{ key: 'Enter' }, 'or', { key: 'Esc' }],
56
+ action: 'Apply or forget what was typed',
57
+ },
58
+ {
59
+ input: [{ key: '1' }, '…', { key: '9' }],
60
+ action: 'Attach a chain of that many carbons',
61
+ },
62
+ {
63
+ input: [{ key: '+' }, 'or', { key: '-' }],
64
+ action: 'Raise or lower the charge',
65
+ },
66
+ { input: [{ key: '.' }], action: 'Add or remove a radical' },
67
+ { input: [{ key: ':' }], action: 'Make it a triplet, then a singlet' },
68
+ { input: [{ key: '?' }], action: 'Make it a connection point' },
69
+ {
70
+ input: [{ key: 'q' }],
71
+ action: 'Edit its query features',
72
+ only: 'fragment',
73
+ },
74
+ { input: [{ key: 'x' }], action: 'Accept any halogen', only: 'fragment' },
75
+ { input: [{ key: 'Delete' }], action: 'Delete it' },
76
+ ],
77
+ note: 'What is typed shows black for an element, blue for a group, grey while incomplete and red when unknown.',
78
+ },
79
+ {
80
+ title: 'Pointer on a bond',
81
+ gestures: [
82
+ {
83
+ input: [{ key: '1' }, { key: '2' }, { key: '3' }],
84
+ action: 'Single, double or triple',
85
+ },
86
+ { input: [{ key: '0' }], action: 'Zero-order (metal–ligand) bond' },
87
+ {
88
+ input: [{ key: 'u' }, 'or', { key: 'd' }],
89
+ action: 'Up or down stereo bond',
90
+ },
91
+ { input: [{ key: 'c' }], action: 'Double bond of unknown geometry' },
92
+ { input: [{ key: 'v' }], action: 'Fuse a 3-membered ring' },
93
+ {
94
+ input: [{ key: '4' }, '…', { key: '7' }],
95
+ action: 'Fuse a ring of that size',
96
+ },
97
+ {
98
+ input: [{ key: 'a' }, 'or', { key: 'b' }],
99
+ action: 'Fuse a benzene ring',
100
+ },
101
+ {
102
+ input: [{ key: 'q' }],
103
+ action: 'Edit its query features',
104
+ only: 'fragment',
105
+ },
106
+ { input: [{ key: 'Delete' }], action: 'Delete it' },
107
+ ],
108
+ },
109
+ {
110
+ title: 'Anywhere',
111
+ gestures: [
112
+ { input: [{ key: 'Mod' }, { key: 'Z' }], action: 'Undo' },
113
+ { input: [{ key: 'Mod' }, { key: 'C' }], action: 'Copy the structure' },
114
+ {
115
+ input: [{ key: 'Mod' }, { key: 'V' }],
116
+ action: 'Paste a molfile, a SMILES or an idCode',
117
+ },
118
+ { input: [{ key: 'Delete' }], action: 'Delete the selection' },
119
+ {
120
+ input: [{ key: 'h' }, 'or', { key: 'v' }],
121
+ action: 'Flip horizontally or vertically',
122
+ only: 'molecule',
123
+ },
124
+ { input: [{ key: 'F1' }], action: 'Open this guide' },
125
+ ],
126
+ note: 'Keys act once the drawing has been clicked. On empty canvas, a key picks the tool whose tooltip shows it.',
127
+ },
128
+ {
129
+ title: 'Selecting',
130
+ gestures: [
131
+ { input: ['Drag'], action: 'Select what the lasso encloses' },
132
+ { input: [{ key: 'Alt' }, '+ drag'], action: 'Select a rectangle' },
133
+ { input: [{ key: 'Shift' }, '+ drag'], action: 'Add to the selection' },
134
+ { input: ['Drag the selection'], action: 'Move it' },
135
+ {
136
+ input: [{ key: 'Shift' }, '+ drag the selection'],
137
+ action: 'Copy it',
138
+ },
139
+ {
140
+ input: ['Double-click'],
141
+ action: 'Edit the query features of an atom, a bond or the selection',
142
+ only: 'fragment',
143
+ },
144
+ ],
145
+ note: 'These need the Select tool, which Space picks.',
146
+ },
147
+ {
148
+ title: 'Stereochemistry',
149
+ gestures: [],
150
+ note: 'Pink bonds flag a stereocentre with too little or too much information. Draw an up or down bond with its narrow end on the centre, then use the enhanced stereo tool to say whether the drawing is this enantiomer (abs), both of them (&) or one of the two (or).',
151
+ },
152
+ ];
153
+
154
+ /** The documentation this guide summarises. */
155
+ export const STRUCTURE_EDITOR_DOCS: readonly EditorGuideLink[] = [
156
+ { title: 'Tools and keys', url: 'https://docs.nmrium.org/help/ocl/' },
157
+ {
158
+ title: 'Atom properties',
159
+ url: 'https://docs.nmrium.org/chemical_structure/ocl/atom-properties/',
160
+ },
161
+ {
162
+ title: 'Stereochemistry',
163
+ url: 'https://docs.nmrium.org/ocl/stereochemistry/',
164
+ },
165
+ ];
166
+
167
+ /** The editor a guide is written for. */
168
+ export interface EditorGuideOptions {
169
+ /**
170
+ * Whether the editor draws one structure or a reaction.
171
+ * @default 'molecule'
172
+ */
173
+ mode?: 'molecule' | 'reaction';
174
+ /**
175
+ * Whether the editor draws a query fragment.
176
+ * @default false
177
+ */
178
+ fragment?: boolean;
179
+ }
180
+
181
+ /**
182
+ * The guide for one editor: gestures that do nothing there are left out, and so
183
+ * is a section left with nothing to say.
184
+ * @param options - The editor the guide is for.
185
+ * @returns The sections to show.
186
+ */
187
+ export function editorGuideSections(
188
+ options: EditorGuideOptions = {},
189
+ ): EditorGuideSection[] {
190
+ const { mode = 'molecule', fragment = false } = options;
191
+ const sections: EditorGuideSection[] = [];
192
+ for (const section of STRUCTURE_EDITOR_GUIDE) {
193
+ const gestures = section.gestures.filter(({ only }) =>
194
+ appliesTo(only, mode, fragment),
195
+ );
196
+ if (gestures.length === 0 && section.note === undefined) continue;
197
+ sections.push({ ...section, gestures });
198
+ }
199
+ return sections;
200
+ }
201
+
202
+ function appliesTo(
203
+ only: EditorGuideScope | undefined,
204
+ mode: 'molecule' | 'reaction',
205
+ fragment: boolean,
206
+ ): boolean {
207
+ if (only === undefined) return true;
208
+ if (only === 'fragment') return fragment;
209
+ return only === mode;
210
+ }
211
+
212
+ /**
213
+ * The text of a key cap.
214
+ * @param key - The key, `Mod` standing for the platform's shortcut modifier.
215
+ * @param isMac - Whether the page runs on a Mac.
216
+ * @returns What the cap shows.
217
+ */
218
+ export function editorKeyLabel(key: string, isMac: boolean): string {
219
+ if (key !== 'Mod') return key;
220
+ return isMac ? '⌘' : 'Ctrl';
221
+ }