react-cheminfo 0.50.0 → 0.52.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 +71 -16
- package/bin/check-seo.mjs +0 -0
- package/lib/clipboard/core/index.d.ts +0 -1
- package/lib/clipboard/core/index.d.ts.map +1 -1
- package/lib/clipboard/core/index.js +0 -1
- package/lib/clipboard/core/index.js.map +1 -1
- package/lib/clipboard/ui/CopyButton.d.ts +6 -0
- package/lib/clipboard/ui/CopyButton.d.ts.map +1 -1
- package/lib/clipboard/ui/CopyButton.js +5 -2
- package/lib/clipboard/ui/CopyButton.js.map +1 -1
- package/lib/color/core/index.d.ts +3 -0
- package/lib/color/core/index.d.ts.map +1 -1
- package/lib/color/core/index.js +2 -0
- package/lib/color/core/index.js.map +1 -1
- package/lib/color/core/interpolate.d.ts +4 -2
- package/lib/color/core/interpolate.d.ts.map +1 -1
- package/lib/color/core/interpolate.js +34 -12
- package/lib/color/core/interpolate.js.map +1 -1
- package/lib/color/core/stops.d.ts +43 -0
- package/lib/color/core/stops.d.ts.map +1 -0
- package/lib/color/core/stops.js +66 -0
- package/lib/color/core/stops.js.map +1 -0
- package/lib/color/core/tone.d.ts +25 -0
- package/lib/color/core/tone.d.ts.map +1 -0
- package/lib/color/core/tone.js +67 -0
- package/lib/color/core/tone.js.map +1 -0
- package/lib/color/core/uniform.d.ts +36 -0
- package/lib/color/core/uniform.d.ts.map +1 -0
- package/lib/color/core/uniform.js +61 -0
- package/lib/color/core/uniform.js.map +1 -0
- package/lib/color/ui/ColorScaleEditor.d.ts +5 -6
- package/lib/color/ui/ColorScaleEditor.d.ts.map +1 -1
- package/lib/color/ui/ColorScaleEditor.js +74 -84
- package/lib/color/ui/ColorScaleEditor.js.map +1 -1
- package/lib/color/ui/ColorScaleSelect.d.ts +16 -0
- package/lib/color/ui/ColorScaleSelect.d.ts.map +1 -1
- package/lib/color/ui/ColorScaleSelect.js +69 -17
- package/lib/color/ui/ColorScaleSelect.js.map +1 -1
- package/lib/color/ui/ColorScaleTone.d.ts +17 -0
- package/lib/color/ui/ColorScaleTone.d.ts.map +1 -0
- package/lib/color/ui/ColorScaleTone.js +55 -0
- package/lib/color/ui/ColorScaleTone.js.map +1 -0
- package/lib/color/ui/ColorScaleTrack.d.ts +29 -0
- package/lib/color/ui/ColorScaleTrack.d.ts.map +1 -0
- package/lib/color/ui/ColorScaleTrack.js +143 -0
- package/lib/color/ui/ColorScaleTrack.js.map +1 -0
- package/lib/download/core/copyFigure.d.ts +14 -0
- package/lib/download/core/copyFigure.d.ts.map +1 -0
- package/lib/download/core/copyFigure.js +18 -0
- package/lib/download/core/copyFigure.js.map +1 -0
- package/lib/download/core/figureHtml.js +1 -0
- package/lib/download/core/figureHtml.js.map +1 -1
- package/lib/download/core/figureHtmlMarkup.d.ts +10 -0
- package/lib/download/core/figureHtmlMarkup.d.ts.map +1 -1
- package/lib/download/core/figureHtmlMarkup.js +7 -1
- package/lib/download/core/figureHtmlMarkup.js.map +1 -1
- package/lib/download/core/index.d.ts +2 -0
- package/lib/download/core/index.d.ts.map +1 -1
- package/lib/download/core/index.js +1 -0
- package/lib/download/core/index.js.map +1 -1
- package/lib/download/ui/FigureDownloadPanel.d.ts +2 -3
- package/lib/download/ui/FigureDownloadPanel.d.ts.map +1 -1
- package/lib/download/ui/FigureDownloadPanel.js +1 -1
- package/lib/download/ui/FigureDownloadPanel.js.map +1 -1
- package/lib/download/ui/figureDownloadHint.d.ts +1 -1
- package/lib/download/ui/figureDownloadHint.d.ts.map +1 -1
- package/lib/download/ui/figureDownloadHint.js.map +1 -1
- package/lib/download/ui/index.d.ts +0 -1
- package/lib/download/ui/index.d.ts.map +1 -1
- package/lib/download/ui/useFigureActions.d.ts +2 -11
- package/lib/download/ui/useFigureActions.d.ts.map +1 -1
- package/lib/download/ui/useFigureActions.js +4 -23
- package/lib/download/ui/useFigureActions.js.map +1 -1
- package/lib/download/ui/useFigureTask.d.ts +21 -0
- package/lib/download/ui/useFigureTask.d.ts.map +1 -0
- package/lib/download/ui/useFigureTask.js +27 -0
- package/lib/download/ui/useFigureTask.js.map +1 -0
- package/lib/host/core/connectHost.d.ts +75 -0
- package/lib/host/core/connectHost.d.ts.map +1 -0
- package/lib/host/core/connectHost.js +171 -0
- package/lib/host/core/connectHost.js.map +1 -0
- package/lib/host/core/hostStructure.d.ts +52 -0
- package/lib/host/core/hostStructure.d.ts.map +1 -0
- package/lib/host/core/hostStructure.js +106 -0
- package/lib/host/core/hostStructure.js.map +1 -0
- package/lib/host/core/index.d.ts +9 -0
- package/lib/host/core/index.d.ts.map +1 -0
- package/lib/host/core/index.js +5 -0
- package/lib/host/core/index.js.map +1 -0
- package/lib/host/core/kinds.d.ts +73 -0
- package/lib/host/core/kinds.d.ts.map +1 -0
- package/lib/host/core/kinds.js +88 -0
- package/lib/host/core/kinds.js.map +1 -0
- package/lib/host/core/protocol.d.ts +134 -0
- package/lib/host/core/protocol.d.ts.map +1 -0
- package/lib/host/core/protocol.js +92 -0
- package/lib/host/core/protocol.js.map +1 -0
- package/lib/host/testing/sciPeaks.d.ts +21 -0
- package/lib/host/testing/sciPeaks.d.ts.map +1 -0
- package/lib/host/testing/sciPeaks.js +62 -0
- package/lib/host/testing/sciPeaks.js.map +1 -0
- package/lib/host/testing/standInHost.d.ts +47 -0
- package/lib/host/testing/standInHost.d.ts.map +1 -0
- package/lib/host/testing/standInHost.js +85 -0
- package/lib/host/testing/standInHost.js.map +1 -0
- package/lib/host/ui/index.d.ts +3 -0
- package/lib/host/ui/index.d.ts.map +1 -0
- package/lib/host/ui/index.js +2 -0
- package/lib/host/ui/index.js.map +1 -0
- package/lib/host/ui/useHostConnection.d.ts +36 -0
- package/lib/host/ui/useHostConnection.d.ts.map +1 -0
- package/lib/host/ui/useHostConnection.js +62 -0
- package/lib/host/ui/useHostConnection.js.map +1 -0
- package/lib/host-testing.d.ts +8 -0
- package/lib/host-testing.d.ts.map +1 -0
- package/lib/host-testing.js +7 -0
- package/lib/host-testing.js.map +1 -0
- package/lib/host.d.ts +11 -0
- package/lib/host.d.ts.map +1 -0
- package/lib/host.js +11 -0
- package/lib/host.js.map +1 -0
- package/lib/i18n/core/chromeCatalog.d.ts +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
- package/lib/locales/de.json +28 -2
- package/lib/locales/en.json +28 -2
- package/lib/locales/es.json +28 -2
- package/lib/locales/fr.json +28 -2
- package/lib/locales/it.json +28 -2
- package/lib/molecule3d/core/index.d.ts +1 -1
- package/lib/molecule3d/core/index.d.ts.map +1 -1
- package/lib/molecule3d/core/index.js +1 -1
- package/lib/molecule3d/core/index.js.map +1 -1
- package/lib/molecule3d/core/settings.d.ts +15 -0
- package/lib/molecule3d/core/settings.d.ts.map +1 -1
- package/lib/molecule3d/core/settings.js +25 -0
- package/lib/molecule3d/core/settings.js.map +1 -1
- package/lib/molecule3d/ui/Molecule3DExport.d.ts +3 -0
- package/lib/molecule3d/ui/Molecule3DExport.d.ts.map +1 -1
- package/lib/molecule3d/ui/Molecule3DExport.js +10 -16
- package/lib/molecule3d/ui/Molecule3DExport.js.map +1 -1
- package/lib/molecule3d/ui/Molecule3DOptions.d.ts +3 -1
- package/lib/molecule3d/ui/Molecule3DOptions.d.ts.map +1 -1
- package/lib/molecule3d/ui/Molecule3DOptions.js +22 -6
- package/lib/molecule3d/ui/Molecule3DOptions.js.map +1 -1
- package/lib/molecule3d/ui/MoleculeCanvas3D.d.ts.map +1 -1
- package/lib/molecule3d/ui/MoleculeCanvas3D.js +4 -3
- package/lib/molecule3d/ui/MoleculeCanvas3D.js.map +1 -1
- package/lib/molecule3d/ui/exportMoleculeImage.d.ts +9 -0
- package/lib/molecule3d/ui/exportMoleculeImage.d.ts.map +1 -1
- package/lib/molecule3d/ui/exportMoleculeImage.js +15 -0
- package/lib/molecule3d/ui/exportMoleculeImage.js.map +1 -1
- package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts +2 -1
- package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts.map +1 -1
- package/lib/molecule3d/ui/useImageExport.d.ts +7 -1
- package/lib/molecule3d/ui/useImageExport.d.ts.map +1 -1
- package/lib/molecule3d/ui/useImageExport.js +12 -3
- package/lib/molecule3d/ui/useImageExport.js.map +1 -1
- package/lib/overlay/ui/FigureBar.d.ts +41 -0
- package/lib/overlay/ui/FigureBar.d.ts.map +1 -0
- package/lib/overlay/ui/FigureBar.js +43 -0
- package/lib/overlay/ui/FigureBar.js.map +1 -0
- package/lib/overlay/ui/OverlayBar.d.ts +13 -4
- package/lib/overlay/ui/OverlayBar.d.ts.map +1 -1
- package/lib/overlay/ui/OverlayBar.js +2 -2
- package/lib/overlay/ui/OverlayBar.js.map +1 -1
- package/lib/overlay/ui/OverlayBarStrip.d.ts +2 -1
- package/lib/overlay/ui/OverlayBarStrip.d.ts.map +1 -1
- package/lib/overlay/ui/OverlayBarStrip.js +9 -5
- package/lib/overlay/ui/OverlayBarStrip.js.map +1 -1
- package/lib/overlay/ui/OverlayIconButton.d.ts +8 -2
- package/lib/overlay/ui/OverlayIconButton.d.ts.map +1 -1
- package/lib/overlay/ui/OverlayIconButton.js +2 -1
- package/lib/overlay/ui/OverlayIconButton.js.map +1 -1
- package/lib/overlay/ui/index.d.ts +4 -0
- package/lib/overlay/ui/index.d.ts.map +1 -1
- package/lib/overlay/ui/index.js +2 -0
- package/lib/overlay/ui/index.js.map +1 -1
- package/lib/overlay/ui/overlayBarShape.d.ts +18 -0
- package/lib/overlay/ui/overlayBarShape.d.ts.map +1 -1
- package/lib/overlay/ui/overlayStyles.d.ts +30 -0
- package/lib/overlay/ui/overlayStyles.d.ts.map +1 -1
- package/lib/overlay/ui/overlayStyles.js +52 -0
- package/lib/overlay/ui/overlayStyles.js.map +1 -1
- package/lib/overlay/ui/useFigureBarAwake.d.ts +12 -0
- package/lib/overlay/ui/useFigureBarAwake.d.ts.map +1 -0
- package/lib/overlay/ui/useFigureBarAwake.js +51 -0
- package/lib/overlay/ui/useFigureBarAwake.js.map +1 -0
- package/lib/periodic/core/elements.d.ts +6 -3
- package/lib/periodic/core/elements.d.ts.map +1 -1
- package/lib/periodic/core/elements.js +9 -3
- package/lib/periodic/core/elements.js.map +1 -1
- package/lib/periodic/core/index.d.ts +2 -2
- package/lib/periodic/core/index.d.ts.map +1 -1
- package/lib/periodic/core/index.js +1 -1
- package/lib/periodic/core/index.js.map +1 -1
- package/lib/periodic/core/layout.d.ts +33 -3
- package/lib/periodic/core/layout.d.ts.map +1 -1
- package/lib/periodic/core/layout.js +14 -0
- package/lib/periodic/core/layout.js.map +1 -1
- package/lib/periodic/ui/BlockStrip.d.ts +20 -0
- package/lib/periodic/ui/BlockStrip.d.ts.map +1 -0
- package/lib/periodic/ui/BlockStrip.js +48 -0
- package/lib/periodic/ui/BlockStrip.js.map +1 -0
- package/lib/periodic/ui/ElementCell.d.ts.map +1 -1
- package/lib/periodic/ui/ElementCell.js +9 -8
- 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 +3 -1
- package/lib/periodic/ui/PeriodicTable.js.map +1 -1
- package/lib/periodic/ui/PeriodicTableHelp.d.ts +33 -0
- package/lib/periodic/ui/PeriodicTableHelp.d.ts.map +1 -0
- package/lib/periodic/ui/PeriodicTableHelp.js +107 -0
- package/lib/periodic/ui/PeriodicTableHelp.js.map +1 -0
- package/lib/periodic/ui/cellStyles.d.ts +3 -2
- package/lib/periodic/ui/cellStyles.d.ts.map +1 -1
- package/lib/periodic/ui/cellStyles.js +3 -2
- package/lib/periodic/ui/cellStyles.js.map +1 -1
- package/lib/periodic/ui/index.d.ts +2 -0
- package/lib/periodic/ui/index.d.ts.map +1 -1
- package/lib/periodic/ui/index.js +1 -0
- package/lib/periodic/ui/index.js.map +1 -1
- package/lib/periodic/ui/periodicTableProps.d.ts +5 -4
- package/lib/periodic/ui/periodicTableProps.d.ts.map +1 -1
- package/lib/periodic/ui/tableLayout.d.ts +3 -1
- package/lib/periodic/ui/tableLayout.d.ts.map +1 -1
- package/lib/periodic/ui/tableLayout.js +8 -3
- package/lib/periodic/ui/tableLayout.js.map +1 -1
- package/lib/share/ui/ShareDialogContent.d.ts +3 -2
- package/lib/share/ui/ShareDialogContent.d.ts.map +1 -1
- package/lib/share/ui/ShareDialogContent.js +5 -6
- package/lib/share/ui/ShareDialogContent.js.map +1 -1
- package/lib/share/ui/ShareLinkBar.d.ts +8 -2
- package/lib/share/ui/ShareLinkBar.d.ts.map +1 -1
- package/lib/share/ui/ShareLinkBar.js +6 -5
- package/lib/share/ui/ShareLinkBar.js.map +1 -1
- package/lib/share/ui/SharePreview.d.ts.map +1 -1
- package/lib/share/ui/SharePreview.js +2 -2
- package/lib/share/ui/SharePreview.js.map +1 -1
- package/lib/structure/ui/StructurePicturePane.js +3 -3
- package/lib/structure/ui/StructurePicturePane.js.map +1 -1
- package/package.json +4 -1
- package/src/clipboard/core/index.ts +0 -1
- package/src/clipboard/ui/CopyButton.tsx +12 -1
- package/src/color/core/index.ts +9 -0
- package/src/color/core/interpolate.ts +51 -22
- package/src/color/core/stops.ts +98 -0
- package/src/color/core/tone.ts +79 -0
- package/src/color/core/uniform.ts +67 -0
- package/src/color/ui/ColorScaleEditor.tsx +115 -141
- package/src/color/ui/ColorScaleSelect.tsx +160 -36
- package/src/color/ui/ColorScaleTone.tsx +103 -0
- package/src/color/ui/ColorScaleTrack.tsx +239 -0
- package/src/download/core/copyFigure.ts +20 -0
- package/src/download/core/figureHtml.ts +1 -0
- package/src/download/core/figureHtmlMarkup.ts +14 -1
- package/src/download/core/index.ts +2 -0
- package/src/download/ui/FigureDownloadPanel.tsx +8 -11
- package/src/download/ui/figureDownloadHint.ts +1 -2
- package/src/download/ui/index.ts +0 -1
- package/src/download/ui/useFigureActions.ts +6 -42
- package/src/download/ui/useFigureTask.ts +46 -0
- package/src/host/core/connectHost.ts +240 -0
- package/src/host/core/hostStructure.ts +129 -0
- package/src/host/core/index.ts +46 -0
- package/src/host/core/kinds.ts +128 -0
- package/src/host/core/protocol.ts +197 -0
- package/src/host/testing/sciPeaks.ts +66 -0
- package/src/host/testing/standInHost.ts +116 -0
- package/src/host/ui/index.ts +2 -0
- package/src/host/ui/useHostConnection.ts +85 -0
- package/src/host-testing.ts +18 -0
- package/src/host.ts +11 -0
- package/src/locales/de.json +28 -2
- package/src/locales/en.json +28 -2
- package/src/locales/es.json +28 -2
- package/src/locales/fr.json +28 -2
- package/src/locales/it.json +28 -2
- package/src/molecule3d/core/index.ts +2 -0
- package/src/molecule3d/core/settings.ts +35 -0
- package/src/molecule3d/ui/Molecule3DExport.tsx +18 -17
- package/src/molecule3d/ui/Molecule3DOptions.tsx +51 -3
- package/src/molecule3d/ui/MoleculeCanvas3D.tsx +11 -3
- package/src/molecule3d/ui/exportMoleculeImage.ts +20 -0
- package/src/molecule3d/ui/moleculeViewer3DProps.ts +2 -1
- package/src/molecule3d/ui/useImageExport.ts +25 -3
- package/src/overlay/ui/FigureBar.tsx +96 -0
- package/src/overlay/ui/OverlayBar.tsx +15 -5
- package/src/overlay/ui/OverlayBarStrip.tsx +34 -22
- package/src/overlay/ui/OverlayIconButton.tsx +10 -2
- package/src/overlay/ui/index.ts +4 -0
- package/src/overlay/ui/overlayBarShape.ts +19 -0
- package/src/overlay/ui/overlayStyles.ts +61 -0
- package/src/overlay/ui/useFigureBarAwake.ts +56 -0
- package/src/periodic/core/elements.ts +8 -3
- package/src/periodic/core/index.ts +8 -1
- package/src/periodic/core/layout.ts +45 -3
- package/src/periodic/ui/BlockStrip.tsx +81 -0
- package/src/periodic/ui/ElementCell.tsx +9 -8
- package/src/periodic/ui/PeriodicTable.tsx +4 -1
- package/src/periodic/ui/PeriodicTableHelp.tsx +205 -0
- package/src/periodic/ui/cellStyles.ts +3 -2
- package/src/periodic/ui/index.ts +5 -0
- package/src/periodic/ui/periodicTableProps.ts +5 -4
- package/src/periodic/ui/tableLayout.ts +9 -3
- package/src/share/ui/ShareDialogContent.tsx +9 -13
- package/src/share/ui/ShareLinkBar.tsx +16 -4
- package/src/share/ui/SharePreview.tsx +6 -2
- package/src/structure/ui/StructurePicturePane.tsx +3 -3
- package/styles/chrome.css +43 -32
- package/lib/clipboard/core/copyPng.d.ts +0 -13
- package/lib/clipboard/core/copyPng.d.ts.map +0 -1
- package/lib/clipboard/core/copyPng.js +0 -19
- package/lib/clipboard/core/copyPng.js.map +0 -1
- package/src/clipboard/core/copyPng.ts +0 -20
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { clamp } from '../../format/core/clamp.ts';
|
|
2
|
+
|
|
3
|
+
import { parseHexColor, toHexColor } from './hex.ts';
|
|
4
|
+
import type { HsvColor } from './hsv.ts';
|
|
5
|
+
import { hsvToRgb, rgbToHsv } from './hsv.ts';
|
|
6
|
+
import type { ColorStop } from './interpolate.ts';
|
|
7
|
+
|
|
8
|
+
/** How strong and how bright a scale's colours are, taken over all its anchors. */
|
|
9
|
+
export interface ScaleTone {
|
|
10
|
+
/** How far the anchors are from grey, from 0 to 1. */
|
|
11
|
+
saturation: number;
|
|
12
|
+
/** How bright they are, from 0 for black to 1. */
|
|
13
|
+
value: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The saturation and the brightness of a scale's anchors, averaged: what a
|
|
18
|
+
* control setting both for the whole scale opens on.
|
|
19
|
+
* @param stops - The anchors.
|
|
20
|
+
* @returns Their mean saturation and mean value; both 0 when there is no anchor.
|
|
21
|
+
*/
|
|
22
|
+
export function scaleTone(stops: readonly ColorStop[]): ScaleTone {
|
|
23
|
+
const colors = toHsv(stops);
|
|
24
|
+
if (colors.length === 0) return { saturation: 0, value: 0 };
|
|
25
|
+
let saturation = 0;
|
|
26
|
+
let value = 0;
|
|
27
|
+
for (const color of colors) {
|
|
28
|
+
saturation += color.saturation;
|
|
29
|
+
value += color.value;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
saturation: saturation / colors.length,
|
|
33
|
+
value: value / colors.length,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Give every anchor the same saturation, the same brightness, or both, keeping
|
|
39
|
+
* each one's hue — so the scale then only turns round the wheel. A grey anchor
|
|
40
|
+
* has no hue of its own and takes the hue of the nearest one that has.
|
|
41
|
+
* @param stops - The anchors.
|
|
42
|
+
* @param tone - What to set; a field left out keeps each anchor's own.
|
|
43
|
+
* @returns The recoloured anchors, in the same order.
|
|
44
|
+
*/
|
|
45
|
+
export function setScaleTone(
|
|
46
|
+
stops: readonly ColorStop[],
|
|
47
|
+
tone: Partial<ScaleTone>,
|
|
48
|
+
): ColorStop[] {
|
|
49
|
+
const colors = toHsv(stops);
|
|
50
|
+
const result: ColorStop[] = [];
|
|
51
|
+
for (let index = 0; index < stops.length; index++) {
|
|
52
|
+
const stop = stops[index];
|
|
53
|
+
const color = colors[index];
|
|
54
|
+
if (stop === undefined || color === undefined) continue;
|
|
55
|
+
const toned = hsvToRgb({
|
|
56
|
+
hue: hueNear(colors, index),
|
|
57
|
+
saturation: clamp(tone.saturation ?? color.saturation, 0, 1),
|
|
58
|
+
value: clamp(tone.value ?? color.value, 0, 1),
|
|
59
|
+
});
|
|
60
|
+
result.push({ ...stop, color: toHexColor(toned) });
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function toHsv(stops: readonly ColorStop[]): HsvColor[] {
|
|
66
|
+
const colors: HsvColor[] = [];
|
|
67
|
+
for (const stop of stops) colors.push(rgbToHsv(parseHexColor(stop.color)));
|
|
68
|
+
return colors;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function hueNear(colors: readonly HsvColor[], index: number): number {
|
|
72
|
+
for (let distance = 0; distance < colors.length; distance++) {
|
|
73
|
+
const before = colors[index - distance];
|
|
74
|
+
if (before !== undefined && before.saturation > 0) return before.hue;
|
|
75
|
+
const after = colors[index + distance];
|
|
76
|
+
if (after !== undefined && after.saturation > 0) return after.hue;
|
|
77
|
+
}
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { TOKEN } from '../../tokens/core/familyTokens.ts';
|
|
2
|
+
|
|
3
|
+
import { readableInk } from './contrast.ts';
|
|
4
|
+
import { normalizeHexColor } from './hex.ts';
|
|
5
|
+
import type { Swatch } from './interpolate.ts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What a colour-scale picker offering one colour writes for it, and a link
|
|
9
|
+
* carries: every value the same colour, so the figure needs no key. Followed by
|
|
10
|
+
* `-` and a hex colour, e.g. `uniform-1c6e42`, it names that colour.
|
|
11
|
+
*/
|
|
12
|
+
export const UNIFORM_COLOR_SCALE_ID = 'uniform';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The one colour of a uniform scale that names none: the family's border grey
|
|
16
|
+
* rather than white, so a cell still stands out from a white page and from the
|
|
17
|
+
* white ground of a saved figure.
|
|
18
|
+
*/
|
|
19
|
+
export const UNIFORM_SWATCH: Swatch = {
|
|
20
|
+
background: TOKEN.border,
|
|
21
|
+
foreground: TOKEN.text,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const PREFIX = `${UNIFORM_COLOR_SCALE_ID}-`;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether a scale text asks for one colour.
|
|
28
|
+
* @param text - A scale id or a custom scale, as a link carries it.
|
|
29
|
+
* @returns `true` for {@link UNIFORM_COLOR_SCALE_ID}, with or without a colour.
|
|
30
|
+
*/
|
|
31
|
+
export function isUniformColorScale(text: string | undefined): boolean {
|
|
32
|
+
const wanted = (text ?? '').trim().toLowerCase();
|
|
33
|
+
return wanted === UNIFORM_COLOR_SCALE_ID || wanted.startsWith(PREFIX);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The colour a uniform scale text names, and the ink to write on it.
|
|
38
|
+
*
|
|
39
|
+
* A colour with a typo in it falls back to the grey rather than failing, so a
|
|
40
|
+
* link written by hand still opens the figure.
|
|
41
|
+
* @param text - A scale text, e.g. `uniform` or `uniform-1c6e42`.
|
|
42
|
+
* @returns The swatch it names, or {@link UNIFORM_SWATCH}.
|
|
43
|
+
*/
|
|
44
|
+
export function uniformSwatch(text: string | undefined): Swatch {
|
|
45
|
+
const color = uniformColor(text);
|
|
46
|
+
if (color === null) return UNIFORM_SWATCH;
|
|
47
|
+
return { background: color, foreground: readableInk(color) };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Write one colour as the scale text a link carries; the `#` is dropped, since
|
|
52
|
+
* a query string is cut in two at the first one.
|
|
53
|
+
* @param color - The colour, as `#rgb` or `#rrggbb`.
|
|
54
|
+
* @returns E.g. `uniform-1c6e42`, or {@link UNIFORM_COLOR_SCALE_ID} when the colour is not a hex colour.
|
|
55
|
+
*/
|
|
56
|
+
export function formatUniformColorScale(color: string): string {
|
|
57
|
+
const normalized = normalizeHexColor(color);
|
|
58
|
+
return normalized === null
|
|
59
|
+
? UNIFORM_COLOR_SCALE_ID
|
|
60
|
+
: `${PREFIX}${normalized.slice(1)}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function uniformColor(text: string | undefined): string | null {
|
|
64
|
+
const wanted = (text ?? '').trim().toLowerCase();
|
|
65
|
+
if (!wanted.startsWith(PREFIX)) return null;
|
|
66
|
+
return normalizeHexColor(`#${wanted.slice(PREFIX.length)}`);
|
|
67
|
+
}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import { Button, HTMLSelect } from '@blueprintjs/core';
|
|
2
2
|
import type { CSSProperties, ReactElement } from 'react';
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
4
|
|
|
4
|
-
import { formatDecimal } from '../../format/core/numbers.ts';
|
|
5
5
|
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
6
|
+
import { NumberInput } from '../../number/ui/NumberInput.tsx';
|
|
6
7
|
import { normalizeHexColor } from '../core/hex.ts';
|
|
7
8
|
import type {
|
|
8
9
|
ColorInterpolation,
|
|
9
10
|
ColorScale,
|
|
10
11
|
ColorStop,
|
|
11
12
|
} from '../core/interpolate.ts';
|
|
12
|
-
import {
|
|
13
|
-
|
|
13
|
+
import {
|
|
14
|
+
MINIMUM_CUSTOM_STOPS,
|
|
15
|
+
moveColorStop,
|
|
16
|
+
recolorColorStop,
|
|
17
|
+
removeColorStop,
|
|
18
|
+
} from '../core/stops.ts';
|
|
14
19
|
|
|
15
|
-
import {
|
|
20
|
+
import { ColorScaleTone } from './ColorScaleTone.tsx';
|
|
21
|
+
import { ColorScaleTrack } from './ColorScaleTrack.tsx';
|
|
16
22
|
|
|
17
|
-
const MINIMUM_STOPS = 2;
|
|
18
23
|
const POSITION_STEP = 0.01;
|
|
19
|
-
const PREVIEW_HEIGHT = 16;
|
|
20
|
-
const PREVIEW_SAMPLES = 48;
|
|
21
|
-
const POSITION_DECIMALS = 2;
|
|
22
24
|
// A `type="color"` input only takes six hex digits; an anchor that is not a
|
|
23
25
|
// hex colour is shown as black until it is picked again.
|
|
24
26
|
const UNREADABLE_COLOR = '#000000';
|
|
@@ -44,88 +46,108 @@ export interface ColorScaleEditorProps {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
|
-
* A colour scale of the reader's own
|
|
48
|
-
* each one sits, and the path it takes between them.
|
|
49
|
+
* A colour scale of the reader's own, edited on the strip it draws.
|
|
49
50
|
*
|
|
50
|
-
* A
|
|
51
|
-
*
|
|
52
|
-
*
|
|
51
|
+
* A click on the strip adds an anchor in the colour already there, an anchor
|
|
52
|
+
* is dragged along it, and an anchor pointed at goes with Backspace. The
|
|
53
|
+
* anchor last touched shows its colour and position below, to set exactly.
|
|
53
54
|
* @param props - See {@link ColorScaleEditorProps}.
|
|
54
|
-
* @returns The
|
|
55
|
+
* @returns The strip, the selected anchor, and the picker of the path between anchors.
|
|
55
56
|
*/
|
|
56
57
|
export function ColorScaleEditor(props: ColorScaleEditorProps): ReactElement {
|
|
57
58
|
const { className, value, onChange } = props;
|
|
58
59
|
const t = useChromeT();
|
|
59
60
|
const stops = value.stops;
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
const [picked, setPicked] = useState(0);
|
|
62
|
+
const [hovered, setHovered] = useState<number | null>(null);
|
|
63
|
+
const colorInput = useRef<HTMLInputElement>(null);
|
|
64
|
+
const selected = Math.min(picked, stops.length - 1);
|
|
65
|
+
const stop = stops[selected];
|
|
66
|
+
const removable = stops.length > MINIMUM_CUSTOM_STOPS;
|
|
67
|
+
|
|
68
|
+
function write(next: ColorStop[]): void {
|
|
62
69
|
onChange({ stops: next, interpolation: value.interpolation });
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
function remove(index: number): void {
|
|
73
|
+
if (!removable) return;
|
|
74
|
+
write(removeColorStop(stops, index));
|
|
75
|
+
setHovered(null);
|
|
76
|
+
setPicked(Math.max(0, index - 1));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (hovered === null || !removable) return;
|
|
81
|
+
const index = hovered;
|
|
82
|
+
function onKeyDown(event: globalThis.KeyboardEvent): void {
|
|
83
|
+
if (event.key !== 'Backspace' && event.key !== 'Delete') return;
|
|
84
|
+
if (isEditable(event.target)) return;
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
onChange({
|
|
87
|
+
stops: removeColorStop(stops, index),
|
|
88
|
+
interpolation: value.interpolation,
|
|
89
|
+
});
|
|
90
|
+
setHovered(null);
|
|
91
|
+
setPicked(Math.max(0, index - 1));
|
|
92
|
+
}
|
|
93
|
+
globalThis.addEventListener('keydown', onKeyDown);
|
|
94
|
+
return () => {
|
|
95
|
+
globalThis.removeEventListener('keydown', onKeyDown);
|
|
96
|
+
};
|
|
97
|
+
}, [hovered, removable, stops, value.interpolation, onChange]);
|
|
98
|
+
|
|
65
99
|
return (
|
|
66
100
|
<div className={className} style={PANEL_STYLE}>
|
|
67
|
-
<
|
|
101
|
+
<ColorScaleTrack
|
|
68
102
|
scale={value}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
103
|
+
selected={selected}
|
|
104
|
+
hovered={hovered}
|
|
105
|
+
onSelect={setPicked}
|
|
106
|
+
onHover={setHovered}
|
|
107
|
+
onChange={write}
|
|
108
|
+
onRemove={remove}
|
|
109
|
+
onPick={() => {
|
|
110
|
+
colorInput.current?.showPicker();
|
|
111
|
+
}}
|
|
72
112
|
/>
|
|
73
113
|
|
|
74
|
-
<div style={
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}}
|
|
101
|
-
/>
|
|
102
|
-
<span style={POSITION_STYLE}>
|
|
103
|
-
{formatDecimal(stop.position, POSITION_DECIMALS)}
|
|
104
|
-
</span>
|
|
105
|
-
<Button
|
|
106
|
-
icon="cross"
|
|
107
|
-
variant="minimal"
|
|
108
|
-
size="small"
|
|
109
|
-
aria-label={t('color.removeAnchor', { index: index + 1 })}
|
|
110
|
-
disabled={stops.length <= MINIMUM_STOPS}
|
|
111
|
-
onClick={() => {
|
|
112
|
-
write(stops.filter((_, at) => at !== index));
|
|
113
|
-
}}
|
|
114
|
-
/>
|
|
115
|
-
</div>
|
|
116
|
-
))}
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<div style={FOOTER_STYLE}>
|
|
114
|
+
<div style={ROW_STYLE}>
|
|
115
|
+
<input
|
|
116
|
+
ref={colorInput}
|
|
117
|
+
type="color"
|
|
118
|
+
aria-label={t('color.anchorColour', { index: selected + 1 })}
|
|
119
|
+
value={normalizeHexColor(stop?.color ?? '') ?? UNREADABLE_COLOR}
|
|
120
|
+
style={SWATCH_STYLE}
|
|
121
|
+
onChange={(event) => {
|
|
122
|
+
write(recolorColorStop(stops, selected, event.target.value));
|
|
123
|
+
}}
|
|
124
|
+
/>
|
|
125
|
+
<NumberInput
|
|
126
|
+
ariaLabel={t('color.anchorPosition', { index: selected + 1 })}
|
|
127
|
+
value={stop?.position ?? 0}
|
|
128
|
+
min={0}
|
|
129
|
+
max={1}
|
|
130
|
+
step={POSITION_STEP}
|
|
131
|
+
size="small"
|
|
132
|
+
buttons={false}
|
|
133
|
+
style={POSITION_STYLE}
|
|
134
|
+
onChange={(position) => {
|
|
135
|
+
const moved = moveColorStop(stops, selected, position);
|
|
136
|
+
write(moved.stops);
|
|
137
|
+
setPicked(moved.index);
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
120
140
|
<Button
|
|
121
|
-
icon="
|
|
141
|
+
icon="trash"
|
|
142
|
+
variant="minimal"
|
|
122
143
|
size="small"
|
|
123
|
-
|
|
124
|
-
disabled={
|
|
144
|
+
aria-label={t('color.removeAnchor', { index: selected + 1 })}
|
|
145
|
+
disabled={!removable}
|
|
125
146
|
onClick={() => {
|
|
126
|
-
|
|
147
|
+
remove(selected);
|
|
127
148
|
}}
|
|
128
149
|
/>
|
|
150
|
+
<span style={SPACER_STYLE} />
|
|
129
151
|
<HTMLSelect
|
|
130
152
|
aria-label={t('color.path')}
|
|
131
153
|
value={value.interpolation}
|
|
@@ -141,59 +163,22 @@ export function ColorScaleEditor(props: ColorScaleEditorProps): ReactElement {
|
|
|
141
163
|
}}
|
|
142
164
|
/>
|
|
143
165
|
</div>
|
|
144
|
-
</div>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
166
|
|
|
148
|
-
|
|
149
|
-
stops: readonly ColorStop[],
|
|
150
|
-
index: number,
|
|
151
|
-
patch: Partial<ColorStop>,
|
|
152
|
-
): ColorStop[] {
|
|
153
|
-
return stops.map((stop, at) => (at === index ? { ...stop, ...patch } : stop));
|
|
154
|
-
}
|
|
167
|
+
<ColorScaleTone stops={stops} onChange={write} />
|
|
155
168
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
* @param stops - The anchors as they stand.
|
|
160
|
-
* @param index - Which of them is being moved.
|
|
161
|
-
* @param position - Where it is being moved to.
|
|
162
|
-
* @returns The position it is allowed to take.
|
|
163
|
-
*/
|
|
164
|
-
function hold(
|
|
165
|
-
stops: readonly ColorStop[],
|
|
166
|
-
index: number,
|
|
167
|
-
position: number,
|
|
168
|
-
): number {
|
|
169
|
-
if (!Number.isFinite(position)) return stops[index]?.position ?? 0;
|
|
170
|
-
const low = stops[index - 1]?.position ?? 0;
|
|
171
|
-
const high = stops[index + 1]?.position ?? 1;
|
|
172
|
-
return Math.min(high, Math.max(low, position));
|
|
169
|
+
<p style={HINT_STYLE}>{t('color.editorHint')}</p>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
let widest = 0;
|
|
184
|
-
let at = 1;
|
|
185
|
-
for (let index = 1; index < stops.length; index++) {
|
|
186
|
-
const span =
|
|
187
|
-
(stops[index]?.position ?? 0) - (stops[index - 1]?.position ?? 0);
|
|
188
|
-
if (span > widest) {
|
|
189
|
-
widest = span;
|
|
190
|
-
at = index;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
const position =
|
|
194
|
-
((stops[at]?.position ?? 1) + (stops[at - 1]?.position ?? 0)) / 2;
|
|
195
|
-
stops.splice(at, 0, { position, color: colorAt(scale, position) });
|
|
196
|
-
return stops;
|
|
174
|
+
function isEditable(target: EventTarget | null): boolean {
|
|
175
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
176
|
+
return (
|
|
177
|
+
target.isContentEditable ||
|
|
178
|
+
target instanceof HTMLInputElement ||
|
|
179
|
+
target instanceof HTMLTextAreaElement ||
|
|
180
|
+
target instanceof HTMLSelectElement
|
|
181
|
+
);
|
|
197
182
|
}
|
|
198
183
|
|
|
199
184
|
const PANEL_STYLE = {
|
|
@@ -201,18 +186,13 @@ const PANEL_STYLE = {
|
|
|
201
186
|
flexDirection: 'column',
|
|
202
187
|
gap: 10,
|
|
203
188
|
padding: 12,
|
|
204
|
-
minWidth:
|
|
205
|
-
} as const satisfies CSSProperties;
|
|
206
|
-
|
|
207
|
-
const ROWS_STYLE = {
|
|
208
|
-
display: 'flex',
|
|
209
|
-
flexDirection: 'column',
|
|
210
|
-
gap: 4,
|
|
189
|
+
minWidth: 320,
|
|
211
190
|
} as const satisfies CSSProperties;
|
|
212
191
|
|
|
213
192
|
const ROW_STYLE = {
|
|
214
193
|
display: 'flex',
|
|
215
194
|
alignItems: 'center',
|
|
195
|
+
flexWrap: 'wrap',
|
|
216
196
|
gap: 8,
|
|
217
197
|
} as const satisfies CSSProperties;
|
|
218
198
|
|
|
@@ -225,23 +205,17 @@ const SWATCH_STYLE = {
|
|
|
225
205
|
cursor: 'pointer',
|
|
226
206
|
} as const satisfies CSSProperties;
|
|
227
207
|
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
minWidth: 90,
|
|
231
|
-
accentColor: 'var(--accent, currentColor)',
|
|
208
|
+
const POSITION_STYLE = {
|
|
209
|
+
width: 64,
|
|
232
210
|
} as const satisfies CSSProperties;
|
|
233
211
|
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
fontVariantNumeric: 'tabular-nums',
|
|
237
|
-
width: 30,
|
|
238
|
-
textAlign: 'right',
|
|
212
|
+
const SPACER_STYLE = {
|
|
213
|
+
flex: '1 1 auto',
|
|
239
214
|
} as const satisfies CSSProperties;
|
|
240
215
|
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
justifyContent: 'space-between',
|
|
216
|
+
const HINT_STYLE = {
|
|
217
|
+
margin: 0,
|
|
218
|
+
color: 'var(--text-muted)',
|
|
219
|
+
fontSize: 12,
|
|
220
|
+
lineHeight: 1.4,
|
|
247
221
|
} as const satisfies CSSProperties;
|