react-cheminfo 0.34.0 → 0.36.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.
- package/README.md +28 -28
- package/bin/check-seo.mjs +333 -0
- package/lib/about/core/about.d.ts +1 -30
- package/lib/about/core/about.d.ts.map +1 -1
- package/lib/about/core/about.js +0 -16
- package/lib/about/core/about.js.map +1 -1
- package/lib/about/core/index.d.ts +0 -1
- package/lib/about/core/index.d.ts.map +1 -1
- package/lib/about/core/index.js +0 -1
- package/lib/about/core/index.js.map +1 -1
- package/lib/about/ui/AboutBuild.d.ts +2 -5
- package/lib/about/ui/AboutBuild.d.ts.map +1 -1
- package/lib/about/ui/AboutBuild.js +7 -19
- package/lib/about/ui/AboutBuild.js.map +1 -1
- package/lib/about/ui/AboutPage.d.ts +2 -2
- package/lib/about/ui/AboutPage.d.ts.map +1 -1
- package/lib/about/ui/AboutPage.js +3 -14
- package/lib/about/ui/AboutPage.js.map +1 -1
- package/lib/about/ui/AboutVersion.d.ts +0 -4
- package/lib/about/ui/AboutVersion.d.ts.map +1 -1
- package/lib/about/ui/AboutVersion.js +3 -31
- package/lib/about/ui/AboutVersion.js.map +1 -1
- package/lib/download/core/index.d.ts +0 -5
- package/lib/download/core/index.d.ts.map +1 -1
- package/lib/download/core/index.js +0 -3
- package/lib/download/core/index.js.map +1 -1
- package/lib/download/ui/FigureDownload.d.ts +4 -55
- package/lib/download/ui/FigureDownload.d.ts.map +1 -1
- package/lib/download/ui/FigureDownload.js +6 -66
- package/lib/download/ui/FigureDownload.js.map +1 -1
- package/lib/download/ui/FigureDownloadPanel.d.ts +16 -2
- package/lib/download/ui/FigureDownloadPanel.d.ts.map +1 -1
- package/lib/download/ui/FigureDownloadPanel.js +11 -30
- package/lib/download/ui/FigureDownloadPanel.js.map +1 -1
- package/lib/download/ui/figureDownloadHint.d.ts +30 -0
- package/lib/download/ui/figureDownloadHint.d.ts.map +1 -0
- package/lib/download/ui/figureDownloadHint.js +33 -0
- package/lib/download/ui/figureDownloadHint.js.map +1 -0
- package/lib/download/ui/index.d.ts +3 -10
- package/lib/download/ui/index.d.ts.map +1 -1
- package/lib/download/ui/index.js +1 -5
- package/lib/download/ui/index.js.map +1 -1
- package/lib/download/ui/useFigureActions.d.ts +50 -0
- package/lib/download/ui/useFigureActions.d.ts.map +1 -0
- package/lib/download/ui/useFigureActions.js +58 -0
- package/lib/download/ui/useFigureActions.js.map +1 -0
- package/lib/download/ui/useFigureDownload.d.ts +85 -0
- package/lib/download/ui/useFigureDownload.d.ts.map +1 -0
- package/lib/download/ui/useFigureDownload.js +75 -0
- package/lib/download/ui/useFigureDownload.js.map +1 -0
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +0 -40
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/core/types.d.ts +0 -10
- package/lib/ecosystem/core/types.d.ts.map +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
- package/lib/ir/core/irCommands.d.ts +2 -2
- package/lib/ir/core/irCommands.js +2 -2
- package/lib/ir/core/irCommands.js.map +1 -1
- package/lib/ir/ui/IrAboutDialog.d.ts.map +1 -1
- package/lib/ir/ui/IrAboutDialog.js +1 -1
- package/lib/ir/ui/IrAboutDialog.js.map +1 -1
- package/lib/ir/ui/IrCanvas.d.ts +6 -0
- package/lib/ir/ui/IrCanvas.d.ts.map +1 -1
- package/lib/ir/ui/IrCanvas.js +2 -2
- package/lib/ir/ui/IrCanvas.js.map +1 -1
- package/lib/ir/ui/IrShell.d.ts.map +1 -1
- package/lib/ir/ui/IrShell.js +16 -11
- package/lib/ir/ui/IrShell.js.map +1 -1
- package/lib/ir/ui/IrToolbar.d.ts +11 -1
- package/lib/ir/ui/IrToolbar.d.ts.map +1 -1
- package/lib/ir/ui/IrToolbar.js +12 -2
- package/lib/ir/ui/IrToolbar.js.map +1 -1
- package/lib/ir/ui/index.d.ts +1 -0
- package/lib/ir/ui/index.d.ts.map +1 -1
- package/lib/ir/ui/index.js.map +1 -1
- package/lib/language/core/index.d.ts +2 -0
- package/lib/language/core/index.d.ts.map +1 -1
- package/lib/language/core/index.js +1 -0
- package/lib/language/core/index.js.map +1 -1
- package/lib/language/core/languagePath.d.ts +52 -0
- package/lib/language/core/languagePath.d.ts.map +1 -0
- package/lib/language/core/languagePath.js +65 -0
- package/lib/language/core/languagePath.js.map +1 -0
- package/lib/locales/de.json +2 -4
- package/lib/locales/en.json +2 -4
- package/lib/locales/es.json +2 -4
- package/lib/locales/fr.json +2 -4
- package/lib/locales/it.json +2 -4
- package/lib/overlay/ui/OverlayPanel.d.ts +6 -0
- package/lib/overlay/ui/OverlayPanel.d.ts.map +1 -1
- package/lib/overlay/ui/OverlayPanel.js +2 -2
- package/lib/overlay/ui/OverlayPanel.js.map +1 -1
- package/lib/overlay/ui/overlayPanelStyles.d.ts +2 -1
- package/lib/overlay/ui/overlayPanelStyles.d.ts.map +1 -1
- package/lib/overlay/ui/overlayPanelStyles.js +3 -1
- package/lib/overlay/ui/overlayPanelStyles.js.map +1 -1
- package/lib/periodic/ui/ElementCell.d.ts +0 -6
- package/lib/periodic/ui/ElementCell.d.ts.map +1 -1
- package/lib/periodic/ui/ElementCell.js +34 -30
- package/lib/periodic/ui/ElementCell.js.map +1 -1
- package/lib/periodic/ui/PeriodicTable.d.ts.map +1 -1
- package/lib/periodic/ui/PeriodicTable.js +12 -15
- package/lib/periodic/ui/PeriodicTable.js.map +1 -1
- package/lib/seo/core/alternates.d.ts +40 -0
- package/lib/seo/core/alternates.d.ts.map +1 -0
- package/lib/seo/core/alternates.js +42 -0
- package/lib/seo/core/alternates.js.map +1 -0
- package/lib/seo/core/index.d.ts +3 -0
- package/lib/seo/core/index.d.ts.map +1 -1
- package/lib/seo/core/index.js +2 -0
- package/lib/seo/core/index.js.map +1 -1
- package/lib/seo/core/noscript.d.ts +9 -0
- package/lib/seo/core/noscript.d.ts.map +1 -1
- package/lib/seo/core/noscript.js +7 -2
- package/lib/seo/core/noscript.js.map +1 -1
- package/lib/seo/core/pageMeta.d.ts +23 -0
- package/lib/seo/core/pageMeta.d.ts.map +1 -1
- package/lib/seo/core/pageMeta.js +52 -3
- package/lib/seo/core/pageMeta.js.map +1 -1
- package/lib/seo/core/pageProse.d.ts +76 -0
- package/lib/seo/core/pageProse.d.ts.map +1 -0
- package/lib/seo/core/pageProse.js +92 -0
- package/lib/seo/core/pageProse.js.map +1 -0
- package/lib/seo/core/routeProblems.d.ts +33 -0
- package/lib/seo/core/routeProblems.d.ts.map +1 -0
- package/lib/seo/core/routeProblems.js +89 -0
- package/lib/seo/core/routeProblems.js.map +1 -0
- package/lib/seo/core/routes.d.ts +14 -0
- package/lib/seo/core/routes.d.ts.map +1 -1
- package/lib/seo/core/routes.js.map +1 -1
- package/lib/seo/core/siteFiles.d.ts.map +1 -1
- package/lib/seo/core/siteFiles.js +2 -1
- package/lib/seo/core/siteFiles.js.map +1 -1
- package/lib/seo/vite/prerender.d.ts +13 -0
- package/lib/seo/vite/prerender.d.ts.map +1 -1
- package/lib/seo/vite/prerender.js +17 -10
- package/lib/seo/vite/prerender.js.map +1 -1
- package/package.json +2 -1
- package/src/about/core/about.ts +1 -51
- package/src/about/core/index.ts +0 -1
- package/src/about/ui/AboutBuild.tsx +8 -37
- package/src/about/ui/AboutPage.tsx +8 -64
- package/src/about/ui/AboutVersion.tsx +5 -45
- package/src/download/core/index.ts +0 -25
- package/src/download/ui/FigureDownload.tsx +11 -148
- package/src/download/ui/FigureDownloadPanel.tsx +46 -45
- package/src/download/ui/figureDownloadHint.ts +52 -0
- package/src/download/ui/index.ts +6 -10
- package/src/download/ui/useFigureActions.ts +113 -0
- package/src/download/ui/useFigureDownload.tsx +191 -0
- package/src/ecosystem/core/sites.ts +0 -40
- package/src/ecosystem/core/types.ts +0 -10
- package/src/ir/core/irCommands.ts +2 -2
- package/src/ir/ui/IrAboutDialog.tsx +0 -13
- package/src/ir/ui/IrCanvas.tsx +8 -2
- package/src/ir/ui/IrShell.tsx +23 -25
- package/src/ir/ui/IrToolbar.tsx +36 -6
- package/src/ir/ui/index.ts +1 -0
- package/src/language/core/index.ts +2 -0
- package/src/language/core/languagePath.ts +78 -0
- package/src/locales/de.json +2 -4
- package/src/locales/en.json +2 -4
- package/src/locales/es.json +2 -4
- package/src/locales/fr.json +2 -4
- package/src/locales/it.json +2 -4
- package/src/overlay/ui/OverlayPanel.tsx +8 -2
- package/src/overlay/ui/overlayPanelStyles.ts +3 -0
- package/src/periodic/ui/ElementCell.tsx +34 -37
- package/src/periodic/ui/PeriodicTable.tsx +12 -17
- package/src/seo/core/alternates.ts +65 -0
- package/src/seo/core/index.ts +3 -0
- package/src/seo/core/noscript.ts +18 -2
- package/src/seo/core/pageMeta.ts +74 -3
- package/src/seo/core/pageProse.ts +146 -0
- package/src/seo/core/routeProblems.ts +111 -0
- package/src/seo/core/routes.ts +14 -0
- package/src/seo/core/siteFiles.ts +2 -1
- package/src/seo/vite/prerender.ts +30 -11
- package/lib/about/core/repository.d.ts +0 -14
- package/lib/about/core/repository.d.ts.map +0 -1
- package/lib/about/core/repository.js +0 -21
- package/lib/about/core/repository.js.map +0 -1
- package/lib/download/core/exportStyles.d.ts +0 -22
- package/lib/download/core/exportStyles.d.ts.map +0 -1
- package/lib/download/core/exportStyles.js +0 -19
- package/lib/download/core/exportStyles.js.map +0 -1
- package/lib/download/core/imageResolution.d.ts +0 -88
- package/lib/download/core/imageResolution.d.ts.map +0 -1
- package/lib/download/core/imageResolution.js +0 -77
- package/lib/download/core/imageResolution.js.map +0 -1
- package/lib/download/core/pictureExport.d.ts +0 -93
- package/lib/download/core/pictureExport.d.ts.map +0 -1
- package/lib/download/core/pictureExport.js +0 -116
- package/lib/download/core/pictureExport.js.map +0 -1
- package/lib/download/ui/ExportDialogFrame.d.ts +0 -46
- package/lib/download/ui/ExportDialogFrame.d.ts.map +0 -1
- package/lib/download/ui/ExportDialogFrame.js +0 -24
- package/lib/download/ui/ExportDialogFrame.js.map +0 -1
- package/lib/download/ui/ExportImageDialog.d.ts +0 -47
- package/lib/download/ui/ExportImageDialog.d.ts.map +0 -1
- package/lib/download/ui/ExportImageDialog.js +0 -25
- package/lib/download/ui/ExportImageDialog.js.map +0 -1
- package/lib/download/ui/ExportNameField.d.ts +0 -29
- package/lib/download/ui/ExportNameField.d.ts.map +0 -1
- package/lib/download/ui/ExportNameField.js +0 -99
- package/lib/download/ui/ExportNameField.js.map +0 -1
- package/lib/download/ui/ExportPictureSection.d.ts +0 -38
- package/lib/download/ui/ExportPictureSection.d.ts.map +0 -1
- package/lib/download/ui/ExportPictureSection.js +0 -48
- package/lib/download/ui/ExportPictureSection.js.map +0 -1
- package/lib/download/ui/useDrawingBox.d.ts +0 -23
- package/lib/download/ui/useDrawingBox.d.ts.map +0 -1
- package/lib/download/ui/useDrawingBox.js +0 -21
- package/lib/download/ui/useDrawingBox.js.map +0 -1
- package/src/about/core/repository.ts +0 -22
- package/src/download/core/exportStyles.ts +0 -20
- package/src/download/core/imageResolution.ts +0 -118
- package/src/download/core/pictureExport.ts +0 -207
- package/src/download/ui/ExportDialogFrame.tsx +0 -86
- package/src/download/ui/ExportImageDialog.tsx +0 -91
- package/src/download/ui/ExportNameField.tsx +0 -148
- package/src/download/ui/ExportPictureSection.tsx +0 -142
- package/src/download/ui/useDrawingBox.ts +0 -37
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/** The box a picture is taken out of. */
|
|
2
|
-
export interface DrawingBox {
|
|
3
|
-
/** Put on the box the drawing sits in, so it can be found again. */
|
|
4
|
-
attach: (element: HTMLElement | null) => void;
|
|
5
|
-
/**
|
|
6
|
-
* The drawing a picture is taken of, `null` when the box holds none — which
|
|
7
|
-
* is what an editor showing nothing yet, or one whose box has not been
|
|
8
|
-
* mounted, hands the export dialog.
|
|
9
|
-
*/
|
|
10
|
-
getDrawing: () => SVGSVGElement | null;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Find the drawing a picture is to be taken of, when it is asked for.
|
|
14
|
-
*
|
|
15
|
-
* A drawing is laid out again on every resize, so the element itself is never
|
|
16
|
-
* held onto: what is kept is the box it sits in, and the drawing is read out of
|
|
17
|
-
* it at the moment a button is pressed. What counts as the drawing in there —
|
|
18
|
-
* the one element for an editor showing one, the panes composed for an editor
|
|
19
|
-
* showing a stack of charts — is `drawingIn`.
|
|
20
|
-
* @returns The box to attach, and the drawing inside it.
|
|
21
|
-
*/
|
|
22
|
-
export declare function useDrawingBox(): DrawingBox;
|
|
23
|
-
//# sourceMappingURL=useDrawingBox.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDrawingBox.d.ts","sourceRoot":"","sources":["../../../src/download/ui/useDrawingBox.ts"],"names":[],"mappings":"AAIA,yCAAyC;AACzC,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C;;;;OAIG;IACH,UAAU,EAAE,MAAM,aAAa,GAAG,IAAI,CAAC;CACxC;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAU1C"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { useCallback, useRef } from 'react';
|
|
2
|
-
import { drawingIn } from "../core/stackedDrawing.js";
|
|
3
|
-
/**
|
|
4
|
-
* Find the drawing a picture is to be taken of, when it is asked for.
|
|
5
|
-
*
|
|
6
|
-
* A drawing is laid out again on every resize, so the element itself is never
|
|
7
|
-
* held onto: what is kept is the box it sits in, and the drawing is read out of
|
|
8
|
-
* it at the moment a button is pressed. What counts as the drawing in there —
|
|
9
|
-
* the one element for an editor showing one, the panes composed for an editor
|
|
10
|
-
* showing a stack of charts — is `drawingIn`.
|
|
11
|
-
* @returns The box to attach, and the drawing inside it.
|
|
12
|
-
*/
|
|
13
|
-
export function useDrawingBox() {
|
|
14
|
-
const box = useRef(null);
|
|
15
|
-
const attach = useCallback((element) => {
|
|
16
|
-
box.current = element;
|
|
17
|
-
}, []);
|
|
18
|
-
const getDrawing = useCallback(() => drawingIn(box.current), []);
|
|
19
|
-
return { attach, getDrawing };
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=useDrawingBox.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDrawingBox.js","sourceRoot":"","sources":["../../../src/download/ui/useDrawingBox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AActD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,OAA2B,EAAE,EAAE;QACzD,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAEjE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What can be linked to inside a repository, and when.
|
|
3
|
-
*
|
|
4
|
-
* Only GitHub has the addresses the About page builds — a release tag, a
|
|
5
|
-
* commit — and only a repository a visitor can open is worth pointing them at.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** Where our repositories live, and the only host whose paths we know. */
|
|
9
|
-
const GITHUB = 'https://github.com/';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The sources as a base a release or a commit can be appended to.
|
|
13
|
-
* @param repository - Where the sources live.
|
|
14
|
-
* @returns The address without its trailing slash, or `undefined` when it is
|
|
15
|
-
* not a GitHub repository and nothing inside it can be addressed.
|
|
16
|
-
*/
|
|
17
|
-
export function githubSources(repository: string): string | undefined {
|
|
18
|
-
const sources = repository.endsWith('/')
|
|
19
|
-
? repository.slice(0, -1)
|
|
20
|
-
: repository;
|
|
21
|
-
return sources.startsWith(GITHUB) ? sources : undefined;
|
|
22
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A row of buttons with the name box among them, wrapping when the dialog is
|
|
3
|
-
* too narrow for them.
|
|
4
|
-
*/
|
|
5
|
-
export const exportButtonsStyle = {
|
|
6
|
-
display: 'flex',
|
|
7
|
-
flexWrap: 'wrap',
|
|
8
|
-
alignItems: 'center',
|
|
9
|
-
gap: 6,
|
|
10
|
-
} as const;
|
|
11
|
-
|
|
12
|
-
/** One group of an export dialog: what it offers, in a column. */
|
|
13
|
-
export const exportSectionStyle = {
|
|
14
|
-
display: 'flex',
|
|
15
|
-
flexDirection: 'column',
|
|
16
|
-
gap: 8,
|
|
17
|
-
} as const;
|
|
18
|
-
|
|
19
|
-
/** Text that says something about a control rather than being one. */
|
|
20
|
-
export const exportNoteStyle = { fontSize: 12, opacity: 0.7 } as const;
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/** How big a picture is written out, as a multiple of the size it is drawn at. */
|
|
2
|
-
export interface ImageResolution {
|
|
3
|
-
/** Identifies the resolution in a picker. */
|
|
4
|
-
id: string;
|
|
5
|
-
/** What the option reads. */
|
|
6
|
-
label: string;
|
|
7
|
-
/** How many pixels each drawing unit becomes. */
|
|
8
|
-
scale: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The sizes a picture leaves the editor at.
|
|
13
|
-
*
|
|
14
|
-
* A drawing unit is about a screen pixel, so the multipliers read as what the
|
|
15
|
-
* picture is for rather than as numbers: what is on screen, the same again on a
|
|
16
|
-
* retina display, and the two steps up a figure needs to survive being printed
|
|
17
|
-
* or thrown on a wall.
|
|
18
|
-
*/
|
|
19
|
-
export const IMAGE_RESOLUTIONS = [
|
|
20
|
-
{ id: 'screen', label: 'Screen (1×)', scale: 1 },
|
|
21
|
-
{ id: 'retina', label: 'Retina (2×)', scale: 2 },
|
|
22
|
-
{ id: 'print', label: 'Print (4×)', scale: 4 },
|
|
23
|
-
{ id: 'poster', label: 'Poster (8×)', scale: 8 },
|
|
24
|
-
] as const satisfies readonly ImageResolution[];
|
|
25
|
-
|
|
26
|
-
/** One of the resolutions offered. */
|
|
27
|
-
export type ImageResolutionId = (typeof IMAGE_RESOLUTIONS)[number]['id'];
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The resolution a picture is written out at unless another is picked: what is
|
|
31
|
-
* on screen, doubled, which is what a picture pasted into a document wants.
|
|
32
|
-
*/
|
|
33
|
-
export const DEFAULT_IMAGE_RESOLUTION = IMAGE_RESOLUTIONS[1];
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Longest side a browser rasterizes reliably.
|
|
37
|
-
*
|
|
38
|
-
* Above roughly this, Safari and Chrome quietly hand back an empty canvas
|
|
39
|
-
* instead of refusing, so the picture would be saved blank.
|
|
40
|
-
*/
|
|
41
|
-
const MAXIMUM_SIDE = 8192;
|
|
42
|
-
|
|
43
|
-
/** Most pixels a rasterized picture is allowed to hold. */
|
|
44
|
-
const MAXIMUM_PIXELS = 32 * 1024 * 1024;
|
|
45
|
-
|
|
46
|
-
/** How big a picture comes out, in pixels. */
|
|
47
|
-
export interface ImageSize {
|
|
48
|
-
/** Width of the picture, in pixels. */
|
|
49
|
-
width: number;
|
|
50
|
-
/** Height of the picture, in pixels. */
|
|
51
|
-
height: number;
|
|
52
|
-
/**
|
|
53
|
-
* What each drawing unit was actually worth. Below the resolution that was
|
|
54
|
-
* asked for when it would have made a picture too big to rasterize.
|
|
55
|
-
*/
|
|
56
|
-
scale: number;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* How big a rectangle of the drawing comes out at a given resolution.
|
|
61
|
-
*
|
|
62
|
-
* The resolution is a wish rather than an instruction: a wide glycan asked for
|
|
63
|
-
* at 8× would run past what a browser can rasterize and come back blank, so the
|
|
64
|
-
* scale is brought down until the picture fits instead. The scale it settled on
|
|
65
|
-
* comes back with the size, so what will actually be written can be shown
|
|
66
|
-
* before it is.
|
|
67
|
-
* @param box - The rectangle being written out.
|
|
68
|
-
* @param box.width - How wide it is, in drawing units.
|
|
69
|
-
* @param box.height - How tall it is, in drawing units.
|
|
70
|
-
* @param scale - How many pixels each drawing unit should become.
|
|
71
|
-
* @returns The size, and the scale it was reached at.
|
|
72
|
-
*/
|
|
73
|
-
export function imageSize(
|
|
74
|
-
box: { width: number; height: number },
|
|
75
|
-
scale: number,
|
|
76
|
-
): ImageSize {
|
|
77
|
-
const wanted = Number.isFinite(scale) && scale > 0 ? scale : 1;
|
|
78
|
-
const width = Math.max(box.width, 1);
|
|
79
|
-
const height = Math.max(box.height, 1);
|
|
80
|
-
|
|
81
|
-
const limit = Math.min(
|
|
82
|
-
MAXIMUM_SIDE / width,
|
|
83
|
-
MAXIMUM_SIDE / height,
|
|
84
|
-
Math.sqrt(MAXIMUM_PIXELS / (width * height)),
|
|
85
|
-
);
|
|
86
|
-
const used = Math.min(wanted, limit);
|
|
87
|
-
// A picture held back by the limit is rounded down rather than to the nearest
|
|
88
|
-
// pixel, so rounding cannot put it back over what it was held to.
|
|
89
|
-
const toPixels = used < wanted ? Math.floor : Math.round;
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
width: Math.max(1, toPixels(width * used)),
|
|
93
|
-
height: Math.max(1, toPixels(height * used)),
|
|
94
|
-
scale: used,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* The resolution an identifier stands for.
|
|
100
|
-
* @param id - What was picked.
|
|
101
|
-
* @returns The resolution, falling back to the default one.
|
|
102
|
-
*/
|
|
103
|
-
export function imageResolution(id: string): ImageResolution {
|
|
104
|
-
return (
|
|
105
|
-
IMAGE_RESOLUTIONS.find((resolution) => resolution.id === id) ??
|
|
106
|
-
DEFAULT_IMAGE_RESOLUTION
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* A size, written the way it is shown next to the resolution it comes from.
|
|
112
|
-
* @param size - The size to write, `null` when nothing is drawn.
|
|
113
|
-
* @returns The text, e.g. `1440 × 620 px`.
|
|
114
|
-
*/
|
|
115
|
-
export function formatImageSize(size: ImageSize | null): string {
|
|
116
|
-
if (!size) return 'nothing to export';
|
|
117
|
-
return `${size.width} × ${size.height} px`;
|
|
118
|
-
}
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { writeImageToClipboard } from '../../clipboard/core/copyPng.ts';
|
|
2
|
-
|
|
3
|
-
import { downloadBlob } from './downloadBlob.ts';
|
|
4
|
-
import { downloadText } from './downloadText.ts';
|
|
5
|
-
import type { ImageSize } from './imageResolution.ts';
|
|
6
|
-
import { imageSize } from './imageResolution.ts';
|
|
7
|
-
import type { SvgBox } from './svgBounds.ts';
|
|
8
|
-
import { contentBox, elementBox } from './svgBounds.ts';
|
|
9
|
-
import { svgToPng } from './svgToPng.ts';
|
|
10
|
-
import { svgToString } from './svgToString.ts';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Room left around a drawing in a written-out picture, in drawing units.
|
|
14
|
-
*
|
|
15
|
-
* Enough that a name written under the bottom of the drawing, and the stroke
|
|
16
|
-
* around its outermost mark, are inside the picture rather than against its
|
|
17
|
-
* edge.
|
|
18
|
-
*/
|
|
19
|
-
export const PICTURE_PADDING = 12;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* What the picture is of: the drawing, or the element it is drawn in.
|
|
23
|
-
*
|
|
24
|
-
* `content` measures the drawing and ignores the canvas around it, which is
|
|
25
|
-
* what a drawing surface wants — it is mostly empty space, panned and zoomed to
|
|
26
|
-
* wherever it was last left, and none of that belongs in a file. `element`
|
|
27
|
-
* takes the element as it is laid out, which is what a chart wants: its axes
|
|
28
|
-
* are drawn to the box it was given, so the box *is* the picture and measuring
|
|
29
|
-
* the marks inside it would crop the frame to wherever the data happens to
|
|
30
|
-
* reach.
|
|
31
|
-
*/
|
|
32
|
-
export type PictureFrame = 'content' | 'element';
|
|
33
|
-
|
|
34
|
-
export interface PictureOptions {
|
|
35
|
-
/**
|
|
36
|
-
* How many pixels each drawing unit becomes.
|
|
37
|
-
* @default 2
|
|
38
|
-
*/
|
|
39
|
-
scale?: number;
|
|
40
|
-
/**
|
|
41
|
-
* The name saved files take, without their extension.
|
|
42
|
-
* @default 'picture'
|
|
43
|
-
*/
|
|
44
|
-
filename?: string;
|
|
45
|
-
/**
|
|
46
|
-
* What the picture is read out as, in place of the instructions the element
|
|
47
|
-
* carries for whoever is working it.
|
|
48
|
-
* @default 'Drawing'
|
|
49
|
-
*/
|
|
50
|
-
label?: string;
|
|
51
|
-
/**
|
|
52
|
-
* What the picture is of.
|
|
53
|
-
* @default 'content'
|
|
54
|
-
*/
|
|
55
|
-
frame?: PictureFrame;
|
|
56
|
-
/**
|
|
57
|
-
* Room left around the drawing, in drawing units. Ignored when the picture is
|
|
58
|
-
* the element, which is already the size it wants to be.
|
|
59
|
-
* @default PICTURE_PADDING
|
|
60
|
-
*/
|
|
61
|
-
padding?: number;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* How big the drawing comes out at a resolution, before it is written out.
|
|
66
|
-
*
|
|
67
|
-
* What is measured is what the picture will be of rather than whatever is on
|
|
68
|
-
* screen, so the answer does not change when the window is resized or the
|
|
69
|
-
* drawing panned — the size shown next to the resolution is the size of the
|
|
70
|
-
* file that is about to be written.
|
|
71
|
-
* @param svg - The element, as it is rendered.
|
|
72
|
-
* @param scale - How many pixels each drawing unit becomes.
|
|
73
|
-
* @param options - What the picture is of.
|
|
74
|
-
* @returns The size, or `null` when nothing is drawn.
|
|
75
|
-
*/
|
|
76
|
-
export function pictureSize(
|
|
77
|
-
svg: SVGSVGElement | null,
|
|
78
|
-
scale: number,
|
|
79
|
-
options: Pick<PictureOptions, 'frame' | 'padding'> = {},
|
|
80
|
-
): ImageSize | null {
|
|
81
|
-
const box = svg && pictureBox(svg, options);
|
|
82
|
-
return box && imageSize(box, scale);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Put the drawing on the clipboard as a PNG.
|
|
87
|
-
*
|
|
88
|
-
* The rasterization is handed to the clipboard as a promise rather than awaited
|
|
89
|
-
* first: Safari only allows a clipboard write the click itself started.
|
|
90
|
-
* @param svg - The element, as it is rendered.
|
|
91
|
-
* @param options - How big to make it.
|
|
92
|
-
* @returns Whether it was copied. `false` when nothing is drawn, and in a
|
|
93
|
-
* browser whose clipboard takes text only.
|
|
94
|
-
*/
|
|
95
|
-
export async function copyPicture(
|
|
96
|
-
svg: SVGSVGElement | null,
|
|
97
|
-
options: PictureOptions = {},
|
|
98
|
-
): Promise<boolean> {
|
|
99
|
-
const written = describe(svg, options);
|
|
100
|
-
if (!written) return false;
|
|
101
|
-
return await writeImageToClipboard(svgToPng(written.svg, written.png));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Save the drawing as a PNG.
|
|
106
|
-
* @param svg - The element, as it is rendered.
|
|
107
|
-
* @param options - How big to make it, and what to call it.
|
|
108
|
-
* @returns Whether it was saved. `false` when nothing is drawn.
|
|
109
|
-
*/
|
|
110
|
-
export async function savePngPicture(
|
|
111
|
-
svg: SVGSVGElement | null,
|
|
112
|
-
options: PictureOptions = {},
|
|
113
|
-
): Promise<boolean> {
|
|
114
|
-
const written = describe(svg, options);
|
|
115
|
-
if (!written) return false;
|
|
116
|
-
downloadBlob(await svgToPng(written.svg, written.png), `${written.name}.png`);
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Save the drawing as an SVG.
|
|
122
|
-
*
|
|
123
|
-
* The resolution is carried into the file as the size it opens at, but the
|
|
124
|
-
* shapes stay shapes: an SVG printed at any size is drawn again rather than
|
|
125
|
-
* enlarged, which is the reason to prefer it wherever it can go.
|
|
126
|
-
* @param svg - The element, as it is rendered.
|
|
127
|
-
* @param options - How big to make it, and what to call it.
|
|
128
|
-
* @returns Whether it was saved. `false` when nothing is drawn.
|
|
129
|
-
*/
|
|
130
|
-
export function saveSvgPicture(
|
|
131
|
-
svg: SVGSVGElement | null,
|
|
132
|
-
options: PictureOptions = {},
|
|
133
|
-
): boolean {
|
|
134
|
-
const written = describe(svg, options);
|
|
135
|
-
if (!written) return false;
|
|
136
|
-
downloadText(
|
|
137
|
-
svgToString(written.svg, written.png),
|
|
138
|
-
`${written.name}.svg`,
|
|
139
|
-
'image/svg+xml',
|
|
140
|
-
);
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** What a picture is written from: the element, its rectangle and its size. */
|
|
145
|
-
interface WrittenPicture {
|
|
146
|
-
/** The element the picture is taken of. */
|
|
147
|
-
svg: SVGSVGElement;
|
|
148
|
-
/**
|
|
149
|
-
* The part of it to write out, and how big, as both writers take it. The
|
|
150
|
-
* rectangle is `null` for a picture of the element, which is written out
|
|
151
|
-
* through whatever `viewBox` it already carries.
|
|
152
|
-
*/
|
|
153
|
-
png: { box: SvgBox | null; width: number; height: number; label: string };
|
|
154
|
-
/** The name a file holding it takes, without its extension. */
|
|
155
|
-
name: string;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* The rectangle a picture of the element would cover, in drawing units.
|
|
160
|
-
* @param svg - The element, as it is rendered.
|
|
161
|
-
* @param options - What the picture is of.
|
|
162
|
-
* @returns The rectangle, or `null` when nothing is drawn.
|
|
163
|
-
*/
|
|
164
|
-
function pictureBox(
|
|
165
|
-
svg: SVGSVGElement,
|
|
166
|
-
options: Pick<PictureOptions, 'frame' | 'padding'>,
|
|
167
|
-
): SvgBox | null {
|
|
168
|
-
const { frame = 'content', padding = PICTURE_PADDING } = options;
|
|
169
|
-
return frame === 'content' ? contentBox(svg, padding) : elementBox(svg);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Work out what a picture of the element would be.
|
|
174
|
-
* @param svg - The element, as it is rendered.
|
|
175
|
-
* @param options - How big to make it, and what to call it.
|
|
176
|
-
* @returns The element and the rectangle to write out, or `null` when nothing
|
|
177
|
-
* is drawn.
|
|
178
|
-
*/
|
|
179
|
-
function describe(
|
|
180
|
-
svg: SVGSVGElement | null,
|
|
181
|
-
options: PictureOptions,
|
|
182
|
-
): WrittenPicture | null {
|
|
183
|
-
const {
|
|
184
|
-
scale = 2,
|
|
185
|
-
filename = 'picture',
|
|
186
|
-
label = 'Drawing',
|
|
187
|
-
frame = 'content',
|
|
188
|
-
} = options;
|
|
189
|
-
if (!svg) return null;
|
|
190
|
-
|
|
191
|
-
const box = pictureBox(svg, options);
|
|
192
|
-
if (!box) return null;
|
|
193
|
-
|
|
194
|
-
const size = imageSize(box, scale);
|
|
195
|
-
return {
|
|
196
|
-
svg,
|
|
197
|
-
// A picture of the element keeps the `viewBox` the element was drawn with,
|
|
198
|
-
// which is the one thing that makes its own scaling come out right.
|
|
199
|
-
png: {
|
|
200
|
-
box: frame === 'content' ? box : null,
|
|
201
|
-
width: size.width,
|
|
202
|
-
height: size.height,
|
|
203
|
-
label,
|
|
204
|
-
},
|
|
205
|
-
name: filename,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import type { IconName } from '@blueprintjs/core';
|
|
2
|
-
import { Button, Dialog, DialogBody, DialogFooter } from '@blueprintjs/core';
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
4
|
-
import { useState } from 'react';
|
|
5
|
-
|
|
6
|
-
import { useConfirmOnEnter } from '../../panel/ui/useConfirmOnEnter.ts';
|
|
7
|
-
import { exportNoteStyle } from '../core/exportStyles.ts';
|
|
8
|
-
|
|
9
|
-
export interface ExportDialogFrameProps {
|
|
10
|
-
/** Whether the dialog is showing. */
|
|
11
|
-
isOpen: boolean;
|
|
12
|
-
/** What it is about, along the top. */
|
|
13
|
-
title: string;
|
|
14
|
-
/** The icon beside that title. */
|
|
15
|
-
icon: IconName;
|
|
16
|
-
/**
|
|
17
|
-
* The name saved files take, without their extension; the section is keyed on
|
|
18
|
-
* it, so a file saved under a new name is what the name box opens on next
|
|
19
|
-
* time rather than what was typed and then thought better of.
|
|
20
|
-
*/
|
|
21
|
-
filename: string;
|
|
22
|
-
/**
|
|
23
|
-
* How wide the dialog opens, in pixels. A section showing text needs the room
|
|
24
|
-
* to show a molfile line whole; one showing controls does not.
|
|
25
|
-
* @default 560
|
|
26
|
-
*/
|
|
27
|
-
width?: number;
|
|
28
|
-
/**
|
|
29
|
-
* Whether the section is mounted only while the dialog is open, for a section
|
|
30
|
-
* whose work — measuring a picture off the page — is only worth doing while
|
|
31
|
-
* it is being looked at.
|
|
32
|
-
* @default false
|
|
33
|
-
*/
|
|
34
|
-
mountWhileOpen?: boolean;
|
|
35
|
-
/** The section, built with the status setter the footer reads. */
|
|
36
|
-
children: (onStatus: (status: string) => void) => ReactNode;
|
|
37
|
-
/** Called when the dialog is dismissed. */
|
|
38
|
-
onClose: () => void;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The frame every export dialog shares: the section, and what it last did.
|
|
43
|
-
*
|
|
44
|
-
* Each thing the editor writes out gets a dialog of its own rather than one
|
|
45
|
-
* shared dialog of tabs, since a picture, a structure and a sequence are asked
|
|
46
|
-
* for at different moments; what they have in common is only the frame, which
|
|
47
|
-
* is here so the three cannot come to differ in how they close or where they
|
|
48
|
-
* say what they wrote.
|
|
49
|
-
* @param props - Component props.
|
|
50
|
-
* @returns The dialog.
|
|
51
|
-
*/
|
|
52
|
-
export function ExportDialogFrame(props: ExportDialogFrameProps) {
|
|
53
|
-
const {
|
|
54
|
-
isOpen,
|
|
55
|
-
title,
|
|
56
|
-
icon,
|
|
57
|
-
filename,
|
|
58
|
-
width = DEFAULT_WIDTH,
|
|
59
|
-
mountWhileOpen = false,
|
|
60
|
-
children,
|
|
61
|
-
onClose,
|
|
62
|
-
} = props;
|
|
63
|
-
|
|
64
|
-
const [status, setStatus] = useState<string | null>(null);
|
|
65
|
-
|
|
66
|
-
useConfirmOnEnter(isOpen, onClose);
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<Dialog
|
|
70
|
-
isOpen={isOpen}
|
|
71
|
-
title={title}
|
|
72
|
-
icon={icon}
|
|
73
|
-
style={{ width, maxWidth: '92vw' }}
|
|
74
|
-
onClose={onClose}
|
|
75
|
-
>
|
|
76
|
-
<DialogBody key={filename}>
|
|
77
|
-
{mountWhileOpen && !isOpen ? null : children(setStatus)}
|
|
78
|
-
</DialogBody>
|
|
79
|
-
<DialogFooter actions={<Button text="Close" onClick={onClose} />}>
|
|
80
|
-
<span style={exportNoteStyle}>{status}</span>
|
|
81
|
-
</DialogFooter>
|
|
82
|
-
</Dialog>
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const DEFAULT_WIDTH = 560;
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_IMAGE_RESOLUTION } from '../core/imageResolution.ts';
|
|
4
|
-
import type { PictureFrame } from '../core/pictureExport.ts';
|
|
5
|
-
|
|
6
|
-
import { ExportDialogFrame } from './ExportDialogFrame.tsx';
|
|
7
|
-
import { ExportPictureSection } from './ExportPictureSection.tsx';
|
|
8
|
-
|
|
9
|
-
export interface ExportImageDialogProps {
|
|
10
|
-
/** Whether the dialog is showing. */
|
|
11
|
-
isOpen: boolean;
|
|
12
|
-
/**
|
|
13
|
-
* The drawing the picture is taken of, as it is rendered. Read when a button
|
|
14
|
-
* is pressed rather than passed in, since a drawing is laid out again on
|
|
15
|
-
* every resize. `null` when nothing is drawn.
|
|
16
|
-
*/
|
|
17
|
-
getDrawing: () => SVGSVGElement | null;
|
|
18
|
-
/**
|
|
19
|
-
* The name saved files take, without their extension.
|
|
20
|
-
* @default 'picture'
|
|
21
|
-
*/
|
|
22
|
-
filename?: string;
|
|
23
|
-
/**
|
|
24
|
-
* What the picture is read out as in the file it is written to, in place of
|
|
25
|
-
* the instructions the element carries for whoever is working it.
|
|
26
|
-
* @default 'Drawing'
|
|
27
|
-
*/
|
|
28
|
-
label?: string;
|
|
29
|
-
/**
|
|
30
|
-
* Whether the picture is the drawing inside the element — a canvas, which is
|
|
31
|
-
* mostly the empty space around what is on it — or the element as it is laid
|
|
32
|
-
* out, which is what a chart drawn to the box it was given is.
|
|
33
|
-
* @default 'content'
|
|
34
|
-
*/
|
|
35
|
-
frame?: PictureFrame;
|
|
36
|
-
/** Called when the dialog is dismissed. */
|
|
37
|
-
onClose: () => void;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* A drawing as a picture: PNG at a chosen resolution, or SVG.
|
|
42
|
-
*
|
|
43
|
-
* How big it comes out is asked rather than guessed, since only one size could
|
|
44
|
-
* be the default and neither a figure nor a thumbnail is it. SVG is the honest
|
|
45
|
-
* answer wherever it is accepted — it is drawn again at whatever size it is
|
|
46
|
-
* printed — so the resolution there is only the size the file opens at.
|
|
47
|
-
*
|
|
48
|
-
* The same dialog writes out a glycan, a molecule and a spectrum, because none
|
|
49
|
-
* of what it does depends on what is drawn: it is handed an SVG element that is
|
|
50
|
-
* already on the page, and asks it how big it is.
|
|
51
|
-
* @param props - Component props.
|
|
52
|
-
* @returns The dialog.
|
|
53
|
-
*/
|
|
54
|
-
export function ExportImageDialog(props: ExportImageDialogProps) {
|
|
55
|
-
const {
|
|
56
|
-
isOpen,
|
|
57
|
-
getDrawing,
|
|
58
|
-
filename = 'picture',
|
|
59
|
-
label = 'Drawing',
|
|
60
|
-
frame,
|
|
61
|
-
onClose,
|
|
62
|
-
} = props;
|
|
63
|
-
|
|
64
|
-
const [resolutionId, setResolutionId] = useState<string>(
|
|
65
|
-
DEFAULT_IMAGE_RESOLUTION.id,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<ExportDialogFrame
|
|
70
|
-
isOpen={isOpen}
|
|
71
|
-
title="Export as an image"
|
|
72
|
-
icon="media"
|
|
73
|
-
filename={filename}
|
|
74
|
-
width={460}
|
|
75
|
-
mountWhileOpen
|
|
76
|
-
onClose={onClose}
|
|
77
|
-
>
|
|
78
|
-
{(onStatus) => (
|
|
79
|
-
<ExportPictureSection
|
|
80
|
-
getDrawing={getDrawing}
|
|
81
|
-
resolutionId={resolutionId}
|
|
82
|
-
onResolutionChange={setResolutionId}
|
|
83
|
-
filename={filename}
|
|
84
|
-
label={label}
|
|
85
|
-
frame={frame}
|
|
86
|
-
onStatus={onStatus}
|
|
87
|
-
/>
|
|
88
|
-
)}
|
|
89
|
-
</ExportDialogFrame>
|
|
90
|
-
);
|
|
91
|
-
}
|