react-cheminfo 0.10.0 → 0.11.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/lib/chrome/ui/NavLink.d.ts.map +1 -1
- package/lib/chrome/ui/NavLink.js +4 -3
- package/lib/chrome/ui/NavLink.js.map +1 -1
- package/lib/chrome/ui/SiteFooter.d.ts +8 -0
- package/lib/chrome/ui/SiteFooter.d.ts.map +1 -1
- package/lib/chrome/ui/SiteFooter.js +7 -2
- package/lib/chrome/ui/SiteFooter.js.map +1 -1
- package/lib/chrome/ui/SiteHeader.d.ts +11 -0
- package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
- package/lib/chrome/ui/SiteHeader.js +7 -2
- package/lib/chrome/ui/SiteHeader.js.map +1 -1
- package/lib/chrome/ui/navItem.d.ts +12 -0
- package/lib/chrome/ui/navItem.d.ts.map +1 -1
- package/lib/chrome/ui/navItem.js.map +1 -1
- package/lib/core.d.ts +1 -0
- package/lib/core.d.ts.map +1 -1
- package/lib/core.js +1 -0
- package/lib/core.js.map +1 -1
- package/lib/download/core/figureLegend.d.ts +90 -0
- package/lib/download/core/figureLegend.d.ts.map +1 -0
- package/lib/download/core/figureLegend.js +77 -0
- package/lib/download/core/figureLegend.js.map +1 -0
- package/lib/download/core/figureSvg.d.ts.map +1 -1
- package/lib/download/core/figureSvg.js +71 -1
- package/lib/download/core/figureSvg.js.map +1 -1
- package/lib/download/core/figureSvgDocument.d.ts +9 -2
- package/lib/download/core/figureSvgDocument.d.ts.map +1 -1
- package/lib/download/core/figureSvgDocument.js +4 -1
- package/lib/download/core/figureSvgDocument.js.map +1 -1
- package/lib/download/core/figureTarget.d.ts +11 -0
- package/lib/download/core/figureTarget.d.ts.map +1 -1
- package/lib/download/core/figureTarget.js +19 -0
- package/lib/download/core/figureTarget.js.map +1 -1
- package/lib/download/core/index.d.ts +3 -1
- package/lib/download/core/index.d.ts.map +1 -1
- package/lib/download/core/index.js +2 -1
- package/lib/download/core/index.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts +1 -1
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +13 -0
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +5 -0
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/overlay/ui/OverlayLegend.d.ts.map +1 -1
- package/lib/overlay/ui/OverlayLegend.js +1 -1
- package/lib/overlay/ui/OverlayLegend.js.map +1 -1
- package/lib/projection/core/projectionCopy.d.ts +4 -0
- package/lib/projection/core/projectionCopy.d.ts.map +1 -1
- package/lib/projection/core/projectionCopy.js +3 -0
- package/lib/projection/core/projectionCopy.js.map +1 -1
- package/lib/projection/core/projectionOptions.d.ts +12 -0
- package/lib/projection/core/projectionOptions.d.ts.map +1 -1
- package/lib/projection/core/projectionOptions.js +27 -1
- package/lib/projection/core/projectionOptions.js.map +1 -1
- package/lib/projection/core/projectionStrings.d.ts +4 -0
- package/lib/projection/core/projectionStrings.d.ts.map +1 -1
- package/lib/projection/core/projectionStrings.js +14 -0
- package/lib/projection/core/projectionStrings.js.map +1 -1
- package/lib/projection/core/projectionTabDefaults.d.ts.map +1 -1
- package/lib/projection/core/projectionTabDefaults.js +12 -0
- package/lib/projection/core/projectionTabDefaults.js.map +1 -1
- package/lib/projection/core/projectionTabs.d.ts +1 -1
- package/lib/projection/core/projectionTabs.d.ts.map +1 -1
- package/lib/projection/core/projectionTabs.js +9 -0
- package/lib/projection/core/projectionTabs.js.map +1 -1
- package/lib/projection/ui/ProjectionMapTab.d.ts +6 -0
- package/lib/projection/ui/ProjectionMapTab.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionMapTab.js +2 -1
- package/lib/projection/ui/ProjectionMapTab.js.map +1 -1
- package/lib/projection/ui/ProjectionPanel.d.ts +6 -0
- package/lib/projection/ui/ProjectionPanel.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionPanel.js +15 -1
- package/lib/projection/ui/ProjectionPanel.js.map +1 -1
- package/lib/projection/ui/ProjectionReadout.d.ts +7 -0
- package/lib/projection/ui/ProjectionReadout.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionReadout.js +5 -4
- package/lib/projection/ui/ProjectionReadout.js.map +1 -1
- package/lib/projection/ui/ProjectionSpaceControls.d.ts +51 -0
- package/lib/projection/ui/ProjectionSpaceControls.d.ts.map +1 -0
- package/lib/projection/ui/ProjectionSpaceControls.js +71 -0
- package/lib/projection/ui/ProjectionSpaceControls.js.map +1 -0
- package/lib/projection/ui/ProjectionSpaceMore.d.ts +32 -0
- package/lib/projection/ui/ProjectionSpaceMore.d.ts.map +1 -0
- package/lib/projection/ui/ProjectionSpaceMore.js +57 -0
- package/lib/projection/ui/ProjectionSpaceMore.js.map +1 -0
- package/lib/projection/ui/ProjectionSpaceTab.d.ts +98 -0
- package/lib/projection/ui/ProjectionSpaceTab.d.ts.map +1 -0
- package/lib/projection/ui/ProjectionSpaceTab.js +66 -0
- package/lib/projection/ui/ProjectionSpaceTab.js.map +1 -0
- package/lib/projection/ui/ProjectionViewer.d.ts +1 -0
- package/lib/projection/ui/ProjectionViewer.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionViewer.js +2 -1
- package/lib/projection/ui/ProjectionViewer.js.map +1 -1
- package/lib/projection/ui/index.d.ts +1 -0
- package/lib/projection/ui/index.d.ts.map +1 -1
- package/lib/projection/ui/index.js.map +1 -1
- package/lib/projection/ui/projectionBarModel.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarModel.js +3 -2
- package/lib/projection/ui/projectionBarModel.js.map +1 -1
- package/lib/projection/ui/projectionBarReadings.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarReadings.js +17 -0
- package/lib/projection/ui/projectionBarReadings.js.map +1 -1
- package/lib/projection/ui/projectionBarSlots.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarSlots.js +17 -0
- package/lib/projection/ui/projectionBarSlots.js.map +1 -1
- package/lib/projection/ui/projectionCloudChoices.d.ts +18 -0
- package/lib/projection/ui/projectionCloudChoices.d.ts.map +1 -0
- package/lib/projection/ui/projectionCloudChoices.js +19 -0
- package/lib/projection/ui/projectionCloudChoices.js.map +1 -0
- package/lib/projection/ui/projectionEllipsoid.d.ts +38 -0
- package/lib/projection/ui/projectionEllipsoid.d.ts.map +1 -0
- package/lib/projection/ui/projectionEllipsoid.js +71 -0
- package/lib/projection/ui/projectionEllipsoid.js.map +1 -0
- package/lib/projection/ui/projectionMapChrome.d.ts +8 -0
- package/lib/projection/ui/projectionMapChrome.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapChrome.js +10 -6
- package/lib/projection/ui/projectionMapChrome.js.map +1 -1
- package/lib/projection/ui/projectionMapView.d.ts +13 -0
- package/lib/projection/ui/projectionMapView.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapView.js +26 -0
- package/lib/projection/ui/projectionMapView.js.map +1 -1
- package/lib/projection/ui/projectionSpaceModel.d.ts +14 -0
- package/lib/projection/ui/projectionSpaceModel.d.ts.map +1 -0
- package/lib/projection/ui/projectionSpaceModel.js +14 -0
- package/lib/projection/ui/projectionSpaceModel.js.map +1 -0
- package/lib/projection/ui/projectionViewerProps.d.ts +34 -0
- package/lib/projection/ui/projectionViewerProps.d.ts.map +1 -1
- package/lib/scatter/ui/ScatterPlot.d.ts +1 -1
- package/lib/scatter/ui/ScatterPlot.d.ts.map +1 -1
- package/lib/scatter/ui/ScatterPlot.js +18 -2
- package/lib/scatter/ui/ScatterPlot.js.map +1 -1
- package/lib/scatter/ui/lassoGesture.d.ts +14 -2
- package/lib/scatter/ui/lassoGesture.d.ts.map +1 -1
- package/lib/scatter/ui/lassoGesture.js +4 -1
- package/lib/scatter/ui/lassoGesture.js.map +1 -1
- package/lib/scatter/ui/scatterPlotProps.d.ts +34 -0
- package/lib/scatter/ui/scatterPlotProps.d.ts.map +1 -1
- package/lib/scatter/ui/useScatterInteraction.d.ts +9 -0
- package/lib/scatter/ui/useScatterInteraction.d.ts.map +1 -1
- package/lib/scatter/ui/useScatterInteraction.js +9 -1
- package/lib/scatter/ui/useScatterInteraction.js.map +1 -1
- package/lib/scatter3d/core/cloudGesture.d.ts +17 -0
- package/lib/scatter3d/core/cloudGesture.d.ts.map +1 -0
- package/lib/scatter3d/core/cloudGesture.js +2 -0
- package/lib/scatter3d/core/cloudGesture.js.map +1 -0
- package/lib/scatter3d/core/confidenceEllipsoid.d.ts +63 -0
- package/lib/scatter3d/core/confidenceEllipsoid.d.ts.map +1 -0
- package/lib/scatter3d/core/confidenceEllipsoid.js +128 -0
- package/lib/scatter3d/core/confidenceEllipsoid.js.map +1 -0
- package/lib/scatter3d/core/cubeFrame.d.ts +40 -0
- package/lib/scatter3d/core/cubeFrame.d.ts.map +1 -0
- package/lib/scatter3d/core/cubeFrame.js +58 -0
- package/lib/scatter3d/core/cubeFrame.js.map +1 -0
- package/lib/scatter3d/core/cubePoints.d.ts +35 -0
- package/lib/scatter3d/core/cubePoints.d.ts.map +1 -0
- package/lib/scatter3d/core/cubePoints.js +50 -0
- package/lib/scatter3d/core/cubePoints.js.map +1 -0
- package/lib/scatter3d/core/ellipsoidCoverage.d.ts +32 -0
- package/lib/scatter3d/core/ellipsoidCoverage.d.ts.map +1 -0
- package/lib/scatter3d/core/ellipsoidCoverage.js +83 -0
- package/lib/scatter3d/core/ellipsoidCoverage.js.map +1 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.d.ts +42 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.d.ts.map +1 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.js +52 -0
- package/lib/scatter3d/core/ellipsoidSilhouette.js.map +1 -0
- package/lib/scatter3d/core/index.d.ts +29 -0
- package/lib/scatter3d/core/index.d.ts.map +1 -0
- package/lib/scatter3d/core/index.js +22 -0
- package/lib/scatter3d/core/index.js.map +1 -0
- package/lib/scatter3d/core/orbitCamera.d.ts +113 -0
- package/lib/scatter3d/core/orbitCamera.d.ts.map +1 -0
- package/lib/scatter3d/core/orbitCamera.js +104 -0
- package/lib/scatter3d/core/orbitCamera.js.map +1 -0
- package/lib/scatter3d/core/symmetricEigen.d.ts +47 -0
- package/lib/scatter3d/core/symmetricEigen.d.ts.map +1 -0
- package/lib/scatter3d/core/symmetricEigen.js +219 -0
- package/lib/scatter3d/core/symmetricEigen.js.map +1 -0
- package/lib/scatter3d/ui/CloudFrameLayer.d.ts +33 -0
- package/lib/scatter3d/ui/CloudFrameLayer.d.ts.map +1 -0
- package/lib/scatter3d/ui/CloudFrameLayer.js +37 -0
- package/lib/scatter3d/ui/CloudFrameLayer.js.map +1 -0
- package/lib/scatter3d/ui/CloudPointLayer.d.ts +62 -0
- package/lib/scatter3d/ui/CloudPointLayer.d.ts.map +1 -0
- package/lib/scatter3d/ui/CloudPointLayer.js +67 -0
- package/lib/scatter3d/ui/CloudPointLayer.js.map +1 -0
- package/lib/scatter3d/ui/CloudShellLayer.d.ts +52 -0
- package/lib/scatter3d/ui/CloudShellLayer.d.ts.map +1 -0
- package/lib/scatter3d/ui/CloudShellLayer.js +40 -0
- package/lib/scatter3d/ui/CloudShellLayer.js.map +1 -0
- package/lib/scatter3d/ui/ScatterCloud.d.ts +24 -0
- package/lib/scatter3d/ui/ScatterCloud.d.ts.map +1 -0
- package/lib/scatter3d/ui/ScatterCloud.js +189 -0
- package/lib/scatter3d/ui/ScatterCloud.js.map +1 -0
- package/lib/scatter3d/ui/cloudGeometry.d.ts +108 -0
- package/lib/scatter3d/ui/cloudGeometry.d.ts.map +1 -0
- package/lib/scatter3d/ui/cloudGeometry.js +122 -0
- package/lib/scatter3d/ui/cloudGeometry.js.map +1 -0
- package/lib/scatter3d/ui/cloudShellStyles.d.ts +36 -0
- package/lib/scatter3d/ui/cloudShellStyles.d.ts.map +1 -0
- package/lib/scatter3d/ui/cloudShellStyles.js +62 -0
- package/lib/scatter3d/ui/cloudShellStyles.js.map +1 -0
- package/lib/scatter3d/ui/cloudZoom.d.ts +21 -0
- package/lib/scatter3d/ui/cloudZoom.d.ts.map +1 -0
- package/lib/scatter3d/ui/cloudZoom.js +22 -0
- package/lib/scatter3d/ui/cloudZoom.js.map +1 -0
- package/lib/scatter3d/ui/index.d.ts +29 -0
- package/lib/scatter3d/ui/index.d.ts.map +1 -0
- package/lib/scatter3d/ui/index.js +21 -0
- package/lib/scatter3d/ui/index.js.map +1 -0
- package/lib/scatter3d/ui/scatterCloudModel.d.ts +60 -0
- package/lib/scatter3d/ui/scatterCloudModel.d.ts.map +1 -0
- package/lib/scatter3d/ui/scatterCloudModel.js +66 -0
- package/lib/scatter3d/ui/scatterCloudModel.js.map +1 -0
- package/lib/scatter3d/ui/scatterCloudProps.d.ts +185 -0
- package/lib/scatter3d/ui/scatterCloudProps.d.ts.map +1 -0
- package/lib/scatter3d/ui/scatterCloudProps.js +9 -0
- package/lib/scatter3d/ui/scatterCloudProps.js.map +1 -0
- package/lib/scatter3d/ui/useOrbitDrag.d.ts +60 -0
- package/lib/scatter3d/ui/useOrbitDrag.d.ts.map +1 -0
- package/lib/scatter3d/ui/useOrbitDrag.js +129 -0
- package/lib/scatter3d/ui/useOrbitDrag.js.map +1 -0
- package/lib/ui.d.ts +1 -0
- package/lib/ui.d.ts.map +1 -1
- package/lib/ui.js +1 -0
- package/lib/ui.js.map +1 -1
- package/package.json +1 -1
- package/src/chrome/ui/NavLink.tsx +5 -3
- package/src/chrome/ui/SiteFooter.tsx +16 -1
- package/src/chrome/ui/SiteHeader.tsx +19 -1
- package/src/chrome/ui/navItem.ts +12 -0
- package/src/core.ts +1 -0
- package/src/download/core/figureLegend.ts +167 -0
- package/src/download/core/figureSvg.ts +81 -1
- package/src/download/core/figureSvgDocument.ts +13 -3
- package/src/download/core/figureTarget.ts +21 -0
- package/src/download/core/index.ts +8 -0
- package/src/ecosystem/core/sites.ts +14 -0
- package/src/ecosystem/ui/glyphs.tsx +26 -0
- package/src/overlay/ui/OverlayLegend.tsx +7 -1
- package/src/projection/core/projectionCopy.ts +9 -0
- package/src/projection/core/projectionOptions.ts +48 -3
- package/src/projection/core/projectionStrings.ts +16 -0
- package/src/projection/core/projectionTabDefaults.ts +12 -0
- package/src/projection/core/projectionTabs.ts +10 -1
- package/src/projection/ui/ProjectionMapTab.tsx +8 -0
- package/src/projection/ui/ProjectionPanel.tsx +63 -0
- package/src/projection/ui/ProjectionReadout.tsx +16 -6
- package/src/projection/ui/ProjectionSpaceControls.tsx +219 -0
- package/src/projection/ui/ProjectionSpaceMore.tsx +169 -0
- package/src/projection/ui/ProjectionSpaceTab.tsx +217 -0
- package/src/projection/ui/ProjectionViewer.tsx +3 -0
- package/src/projection/ui/index.ts +1 -0
- package/src/projection/ui/projectionBarModel.ts +3 -2
- package/src/projection/ui/projectionBarReadings.ts +18 -0
- package/src/projection/ui/projectionBarSlots.tsx +19 -0
- package/src/projection/ui/projectionCloudChoices.ts +23 -0
- package/src/projection/ui/projectionEllipsoid.ts +78 -0
- package/src/projection/ui/projectionMapChrome.ts +26 -5
- package/src/projection/ui/projectionMapView.ts +45 -0
- package/src/projection/ui/projectionSpaceModel.ts +14 -0
- package/src/projection/ui/projectionViewerProps.ts +35 -0
- package/src/scatter/ui/ScatterPlot.tsx +21 -2
- package/src/scatter/ui/lassoGesture.ts +15 -2
- package/src/scatter/ui/scatterPlotProps.ts +35 -0
- package/src/scatter/ui/useScatterInteraction.ts +22 -1
- package/src/scatter3d/core/cloudGesture.ts +16 -0
- package/src/scatter3d/core/confidenceEllipsoid.ts +177 -0
- package/src/scatter3d/core/cubeFrame.ts +72 -0
- package/src/scatter3d/core/cubePoints.ts +75 -0
- package/src/scatter3d/core/ellipsoidCoverage.ts +86 -0
- package/src/scatter3d/core/ellipsoidSilhouette.ts +76 -0
- package/src/scatter3d/core/index.ts +52 -0
- package/src/scatter3d/core/orbitCamera.ts +159 -0
- package/src/scatter3d/core/symmetricEigen.ts +273 -0
- package/src/scatter3d/ui/CloudFrameLayer.tsx +97 -0
- package/src/scatter3d/ui/CloudPointLayer.tsx +132 -0
- package/src/scatter3d/ui/CloudShellLayer.tsx +107 -0
- package/src/scatter3d/ui/ScatterCloud.tsx +312 -0
- package/src/scatter3d/ui/cloudGeometry.ts +200 -0
- package/src/scatter3d/ui/cloudShellStyles.ts +74 -0
- package/src/scatter3d/ui/cloudZoom.ts +22 -0
- package/src/scatter3d/ui/index.ts +41 -0
- package/src/scatter3d/ui/scatterCloudModel.ts +98 -0
- package/src/scatter3d/ui/scatterCloudProps.ts +191 -0
- package/src/scatter3d/ui/useOrbitDrag.ts +220 -0
- package/src/ui.ts +1 -0
- package/styles/chrome.css +29 -4
|
@@ -21,11 +21,19 @@ export type {
|
|
|
21
21
|
FigureSvgDocumentOptions,
|
|
22
22
|
} from './figureSvgDocument.ts';
|
|
23
23
|
export { figureSvgDocument } from './figureSvgDocument.ts';
|
|
24
|
+
export type {
|
|
25
|
+
FigureLegendCard,
|
|
26
|
+
FigureLegendMark,
|
|
27
|
+
FigureLegendPrint,
|
|
28
|
+
FigureLegendText,
|
|
29
|
+
} from './figureLegend.ts';
|
|
30
|
+
export { figureLegendMarkup } from './figureLegend.ts';
|
|
24
31
|
export type { FigureBounds } from './figureTarget.ts';
|
|
25
32
|
export {
|
|
26
33
|
figureBounds,
|
|
27
34
|
figureDrawings,
|
|
28
35
|
figureElement,
|
|
36
|
+
figureLegends,
|
|
29
37
|
figureSize,
|
|
30
38
|
} from './figureTarget.ts';
|
|
31
39
|
export { sanitizeFileName } from './sanitizeFileName.ts';
|
|
@@ -7,6 +7,7 @@ export type SiteId =
|
|
|
7
7
|
| 'smiles'
|
|
8
8
|
| 'chemcalc'
|
|
9
9
|
| 'nmrium'
|
|
10
|
+
| 'metabo'
|
|
10
11
|
| 'derepflow'
|
|
11
12
|
| 'surge'
|
|
12
13
|
| 'tex'
|
|
@@ -133,6 +134,19 @@ export const ECOSYSTEM_SITES: readonly EcosystemSite[] = [
|
|
|
133
134
|
brandAlt: '#2b143e',
|
|
134
135
|
mark: { plate: '#2b143e', accent: '#ea580c' },
|
|
135
136
|
},
|
|
137
|
+
{
|
|
138
|
+
id: 'metabo',
|
|
139
|
+
// Not `.cheminfo`: the second half of a one-word name is the domain the
|
|
140
|
+
// site actually lives at, and this one is a sibling of NMRium rather than
|
|
141
|
+
// of the cheminfo tools.
|
|
142
|
+
name: { lead: 'metabo', alt: 'nmrium', dot: true },
|
|
143
|
+
host: 'metabo.nmrium.com',
|
|
144
|
+
repository: 'https://github.com/cheminfo/metabo',
|
|
145
|
+
tagline: 'Turn a batch of NMR spectra into a map of your samples.',
|
|
146
|
+
brand: '#4d7c0f',
|
|
147
|
+
brandAlt: '#b91c1c',
|
|
148
|
+
mark: { plate: '#4d7c0f', accent: '#b91c1c' },
|
|
149
|
+
},
|
|
136
150
|
{
|
|
137
151
|
id: 'derepflow',
|
|
138
152
|
name: { lead: 'Derep', alt: 'Flow' },
|
|
@@ -148,6 +148,32 @@ export const GLYPHS: Record<SiteId, (alt: string) => ReactNode> = {
|
|
|
148
148
|
/>
|
|
149
149
|
</g>
|
|
150
150
|
),
|
|
151
|
+
// Two groups of samples with the outline that holds each: the picture the
|
|
152
|
+
// tool exists to draw. One is drawn and one is filled rather than both
|
|
153
|
+
// outlined, because two rings crossing close up into a blob at 16 px —
|
|
154
|
+
// checked at 16, 20, 24, 32 and 64 before this was settled on.
|
|
155
|
+
metabo: (accent) => (
|
|
156
|
+
<>
|
|
157
|
+
<ellipse
|
|
158
|
+
cx="12"
|
|
159
|
+
cy="12.5"
|
|
160
|
+
rx="5.8"
|
|
161
|
+
ry="9"
|
|
162
|
+
fill="none"
|
|
163
|
+
stroke="#ffffff"
|
|
164
|
+
strokeWidth="3"
|
|
165
|
+
transform="rotate(-32 12 12.5)"
|
|
166
|
+
/>
|
|
167
|
+
<ellipse
|
|
168
|
+
cx="20.5"
|
|
169
|
+
cy="20"
|
|
170
|
+
rx="5.4"
|
|
171
|
+
ry="8.6"
|
|
172
|
+
fill={accent}
|
|
173
|
+
transform="rotate(32 20.5 20)"
|
|
174
|
+
/>
|
|
175
|
+
</>
|
|
176
|
+
),
|
|
151
177
|
// One formula branching into every structure it can be.
|
|
152
178
|
surge: (alt) => (
|
|
153
179
|
<>
|
|
@@ -103,7 +103,13 @@ export function OverlayLegend(props: OverlayLegendProps): ReactElement {
|
|
|
103
103
|
const written = folded === 0 ? entries : entries.slice(0, room - 1);
|
|
104
104
|
|
|
105
105
|
return (
|
|
106
|
-
<div
|
|
106
|
+
<div
|
|
107
|
+
style={overlayCardStyle(placement, metrics)}
|
|
108
|
+
// The key is chrome, since its entries are pressed, but it is part of
|
|
109
|
+
// the picture all the same: the export redraws it into the saved file.
|
|
110
|
+
data-figure="legend"
|
|
111
|
+
data-testid={testId}
|
|
112
|
+
>
|
|
107
113
|
<div style={overlayGroundStyle(awake ? 1 : RESTING_OPACITY, busy)} />
|
|
108
114
|
<div
|
|
109
115
|
role="group"
|
|
@@ -29,6 +29,8 @@ export interface ProjectionCopy {
|
|
|
29
29
|
intro: {
|
|
30
30
|
/** The map. */
|
|
31
31
|
map: string;
|
|
32
|
+
/** The cloud. */
|
|
33
|
+
space: string;
|
|
32
34
|
/** The pair grid. */
|
|
33
35
|
pairs: string;
|
|
34
36
|
/**
|
|
@@ -49,6 +51,8 @@ export interface ProjectionCopy {
|
|
|
49
51
|
legend: {
|
|
50
52
|
/** The map. Carries `{groups}` and `{coverage}`. */
|
|
51
53
|
map: string;
|
|
54
|
+
/** The cloud. Carries `{groups}` and `{coverage}`. */
|
|
55
|
+
space: string;
|
|
52
56
|
/** The map with no outlines. Carries `{groups}`. */
|
|
53
57
|
mapNoEllipse: string;
|
|
54
58
|
/** The pair grid. Carries `{groups}`. */
|
|
@@ -126,6 +130,7 @@ export interface ProjectionCopy {
|
|
|
126
130
|
export const PROJECTION_COPY: ProjectionCopy = {
|
|
127
131
|
tab: {
|
|
128
132
|
map: 'Map',
|
|
133
|
+
space: 'Map in 3D',
|
|
129
134
|
pairs: 'Every pair',
|
|
130
135
|
variables: 'What differs',
|
|
131
136
|
shares: 'How much each explains',
|
|
@@ -134,6 +139,8 @@ export const PROJECTION_COPY: ProjectionCopy = {
|
|
|
134
139
|
bar: PROJECTION_BAR_WORDS,
|
|
135
140
|
intro: {
|
|
136
141
|
map: 'Each dot is one sample. Dots that sit together are alike; dots far apart are the ones that differ most. The two axes are the strongest patterns of difference, called components.',
|
|
142
|
+
space:
|
|
143
|
+
'The same map with a third component, in a box you can turn. Two groups that sit on top of each other on the flat map often come apart as soon as the box moves. The frame says which axis is which; the numbers are in the card you get by pointing at a dot.',
|
|
137
144
|
pairs:
|
|
138
145
|
'The same map drawn for every pair of components. A grouping the first two miss often shows up in another pair.',
|
|
139
146
|
variablesContinuous:
|
|
@@ -149,6 +156,8 @@ export const PROJECTION_COPY: ProjectionCopy = {
|
|
|
149
156
|
},
|
|
150
157
|
legend: {
|
|
151
158
|
map: 'Colour = {groups}. Each outline covers about {coverage} of that group, assuming the group is roughly bell-shaped.',
|
|
159
|
+
space:
|
|
160
|
+
'Colour = {groups}. Each shell holds about {coverage} of that group, assuming the group is roughly bell-shaped. A shell has to hold a sample in three directions at once, so it is wider than the outline the same share draws on the flat map.',
|
|
152
161
|
mapNoEllipse: 'Colour = {groups}.',
|
|
153
162
|
pairs:
|
|
154
163
|
'Colour = {groups}. The strip along the diagonal shows how the samples spread out along that component on its own.',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { EllipseSize } from '../../scatter/core/confidenceEllipse.ts';
|
|
2
2
|
import type { ScatterSelectionMode } from '../../scatter/core/scatterSelection.ts';
|
|
3
|
+
import type { CloudGesture } from '../../scatter3d/core/cloudGesture.ts';
|
|
3
4
|
|
|
4
5
|
import type { ProjectionResult } from './projectionResult.ts';
|
|
5
6
|
import { drawableVariablesView } from './variablesView.ts';
|
|
@@ -33,6 +34,17 @@ export interface ProjectionOptions {
|
|
|
33
34
|
* @default 1
|
|
34
35
|
*/
|
|
35
36
|
yAxis: number;
|
|
37
|
+
/**
|
|
38
|
+
* Which axis runs away from the reader, on the cloud alone. The map and the
|
|
39
|
+
* pair grid never read it, so moving it leaves them where they were.
|
|
40
|
+
* @default 2
|
|
41
|
+
*/
|
|
42
|
+
zAxis: number;
|
|
43
|
+
/**
|
|
44
|
+
* What a drag over the cloud does: turn the box, or draw a lasso.
|
|
45
|
+
* @default 'turn'
|
|
46
|
+
*/
|
|
47
|
+
cloudGesture: CloudGesture;
|
|
36
48
|
/**
|
|
37
49
|
* What decides a dot's colour.
|
|
38
50
|
* @default 'group'
|
|
@@ -123,6 +135,8 @@ export type ProjectionOptionId = keyof ProjectionOptions;
|
|
|
123
135
|
export const DEFAULT_PROJECTION_OPTIONS: ProjectionOptions = {
|
|
124
136
|
xAxis: 0,
|
|
125
137
|
yAxis: 1,
|
|
138
|
+
zAxis: 2,
|
|
139
|
+
cloudGesture: 'turn',
|
|
126
140
|
colorBy: 'group',
|
|
127
141
|
ellipse: { kind: 'coverage', probability: 0.95 },
|
|
128
142
|
pointRadius: 3.5,
|
|
@@ -163,14 +177,21 @@ export function resolveProjectionOptions(
|
|
|
163
177
|
const axisCount = result.axes.length;
|
|
164
178
|
const lastAxis = Math.max(0, axisCount - 1);
|
|
165
179
|
const xAxis = clampIndex(pick(overrides, 'xAxis'), lastAxis);
|
|
180
|
+
const yAxis = apart(
|
|
181
|
+
clampIndex(pick(overrides, 'yAxis'), lastAxis),
|
|
182
|
+
xAxis,
|
|
183
|
+
lastAxis,
|
|
184
|
+
);
|
|
166
185
|
|
|
167
186
|
return {
|
|
168
187
|
xAxis,
|
|
169
|
-
yAxis
|
|
170
|
-
|
|
171
|
-
|
|
188
|
+
yAxis,
|
|
189
|
+
zAxis: apartFrom(
|
|
190
|
+
clampIndex(pick(overrides, 'zAxis'), lastAxis),
|
|
191
|
+
[xAxis, yAxis],
|
|
172
192
|
lastAxis,
|
|
173
193
|
),
|
|
194
|
+
cloudGesture: pick(overrides, 'cloudGesture'),
|
|
174
195
|
colorBy: pick(overrides, 'colorBy'),
|
|
175
196
|
ellipse: pick(overrides, 'ellipse'),
|
|
176
197
|
pointRadius: atLeast(pick(overrides, 'pointRadius'), 3.5, 0),
|
|
@@ -223,6 +244,30 @@ function apart(yAxis: number, xAxis: number, lastAxis: number): number {
|
|
|
223
244
|
return xAxis > 0 ? xAxis - 1 : Math.min(1, lastAxis);
|
|
224
245
|
}
|
|
225
246
|
|
|
247
|
+
/**
|
|
248
|
+
* An axis that is none of the ones already drawn.
|
|
249
|
+
*
|
|
250
|
+
* The cloud's third direction has two axes to avoid rather than one, and an
|
|
251
|
+
* axis drawn against itself in a box is a flat map stood on its edge — worse
|
|
252
|
+
* than the diagonal line it is on a map, because the reader cannot see from
|
|
253
|
+
* the picture that it happened.
|
|
254
|
+
* @param wanted - The axis asked for, already inside the result.
|
|
255
|
+
* @param taken - The axes already drawn.
|
|
256
|
+
* @param lastAxis - The highest axis there is.
|
|
257
|
+
* @returns The axis to draw; the lowest one still free when the wanted one is taken, and the wanted one back when every axis is.
|
|
258
|
+
*/
|
|
259
|
+
function apartFrom(
|
|
260
|
+
wanted: number,
|
|
261
|
+
taken: readonly number[],
|
|
262
|
+
lastAxis: number,
|
|
263
|
+
): number {
|
|
264
|
+
if (!taken.includes(wanted)) return wanted;
|
|
265
|
+
for (let axis = 0; axis <= lastAxis; axis++) {
|
|
266
|
+
if (!taken.includes(axis)) return axis;
|
|
267
|
+
}
|
|
268
|
+
return wanted;
|
|
269
|
+
}
|
|
270
|
+
|
|
226
271
|
function clampCount(
|
|
227
272
|
value: number,
|
|
228
273
|
fallback: number,
|
|
@@ -58,6 +58,14 @@ export const PROJECTION_HELP: Record<ProjectionOptionId, ProjectionHelp> = {
|
|
|
58
58
|
title: 'Up',
|
|
59
59
|
body: 'Which pattern of difference runs bottom to top.',
|
|
60
60
|
},
|
|
61
|
+
zAxis: {
|
|
62
|
+
title: 'Into',
|
|
63
|
+
body: 'Which pattern of difference runs away from you, into the box. Only the cloud draws it; the map and the pair grid never read it.',
|
|
64
|
+
},
|
|
65
|
+
cloudGesture: {
|
|
66
|
+
title: 'Drag does',
|
|
67
|
+
body: 'Turning the box and drawing a lasso both want a plain drag, so you say which one it is. Inside a lasso, Shift still adds and Alt still takes away.',
|
|
68
|
+
},
|
|
61
69
|
colorBy: {
|
|
62
70
|
title: 'Colour by',
|
|
63
71
|
body: 'What a dot’s colour stands for. Colouring by nothing is useful when the groups are what you are trying to find rather than what you already know.',
|
|
@@ -137,6 +145,7 @@ export const PROJECTION_HELP: Record<ProjectionOptionId, ProjectionHelp> = {
|
|
|
137
145
|
*/
|
|
138
146
|
export const PROJECTION_SHORT_TAB: Record<ProjectionTab, string> = {
|
|
139
147
|
map: 'Map',
|
|
148
|
+
space: '3D',
|
|
140
149
|
pairs: 'Pairs',
|
|
141
150
|
variables: 'Differs',
|
|
142
151
|
shares: 'Explains',
|
|
@@ -155,6 +164,8 @@ export interface ProjectionBarWords {
|
|
|
155
164
|
colorBy: string;
|
|
156
165
|
/** In front of how much of a group its outline covers. */
|
|
157
166
|
ellipse: string;
|
|
167
|
+
/** In front of what a drag over the cloud does. */
|
|
168
|
+
cloudGesture: string;
|
|
158
169
|
/** In front of how many components the pair grid lays out. */
|
|
159
170
|
pairCount: string;
|
|
160
171
|
/** In front of what the "what differs" panels are drawing. */
|
|
@@ -197,6 +208,7 @@ export const PROJECTION_BAR_WORDS: ProjectionBarWords = {
|
|
|
197
208
|
key: {
|
|
198
209
|
colorBy: 'Colour',
|
|
199
210
|
ellipse: 'Outlines',
|
|
211
|
+
cloudGesture: 'Drag',
|
|
200
212
|
pairCount: 'Components',
|
|
201
213
|
variablesView: 'Show',
|
|
202
214
|
shareTarget: 'Target',
|
|
@@ -228,6 +240,8 @@ export const PROJECTION_BAR_WORDS: ProjectionBarWords = {
|
|
|
228
240
|
export const PROJECTION_PANEL_NAME: Record<ProjectionOptionId, string> = {
|
|
229
241
|
xAxis: 'Across',
|
|
230
242
|
yAxis: 'Up',
|
|
243
|
+
zAxis: 'Into',
|
|
244
|
+
cloudGesture: 'Drag does',
|
|
231
245
|
colorBy: 'Colour by',
|
|
232
246
|
ellipse: 'Outlines',
|
|
233
247
|
pointRadius: 'Dot size',
|
|
@@ -260,6 +274,8 @@ export const PROJECTION_PANEL_SECTION = {
|
|
|
260
274
|
drawing: 'Drawing',
|
|
261
275
|
/** What a drag over the figure does to the selection. */
|
|
262
276
|
selecting: 'Selecting',
|
|
277
|
+
/** What a drag over the cloud does at all, before the selection hears of it. */
|
|
278
|
+
handling: 'Handling',
|
|
263
279
|
/** What the panels of the "what differs" tab are measured against. */
|
|
264
280
|
scale: 'Scale',
|
|
265
281
|
} as const;
|
|
@@ -31,6 +31,18 @@ export const PROJECTION_TAB_DEFAULTS: Record<
|
|
|
31
31
|
showIds: DEFAULT_PROJECTION_OPTIONS.showIds,
|
|
32
32
|
selectMode: DEFAULT_PROJECTION_OPTIONS.selectMode,
|
|
33
33
|
},
|
|
34
|
+
space: {
|
|
35
|
+
xAxis: DEFAULT_PROJECTION_OPTIONS.xAxis,
|
|
36
|
+
yAxis: DEFAULT_PROJECTION_OPTIONS.yAxis,
|
|
37
|
+
zAxis: DEFAULT_PROJECTION_OPTIONS.zAxis,
|
|
38
|
+
cloudGesture: DEFAULT_PROJECTION_OPTIONS.cloudGesture,
|
|
39
|
+
colorBy: DEFAULT_PROJECTION_OPTIONS.colorBy,
|
|
40
|
+
ellipse: DEFAULT_PROJECTION_OPTIONS.ellipse,
|
|
41
|
+
pointRadius: DEFAULT_PROJECTION_OPTIONS.pointRadius,
|
|
42
|
+
showGroupLabels: DEFAULT_PROJECTION_OPTIONS.showGroupLabels,
|
|
43
|
+
showIds: DEFAULT_PROJECTION_OPTIONS.showIds,
|
|
44
|
+
selectMode: DEFAULT_PROJECTION_OPTIONS.selectMode,
|
|
45
|
+
},
|
|
34
46
|
pairs: {
|
|
35
47
|
pairCount: DEFAULT_PROJECTION_OPTIONS.pairCount,
|
|
36
48
|
colorBy: DEFAULT_PROJECTION_OPTIONS.colorBy,
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { ProjectionAxis, ProjectionResult } from './projectionResult.ts';
|
|
2
2
|
|
|
3
3
|
/** Which tab of a projection viewer is showing. The ids are stable, so a site may put one in its own URL. */
|
|
4
|
-
export type ProjectionTab = 'map' | 'pairs' | 'variables' | 'shares';
|
|
4
|
+
export type ProjectionTab = 'map' | 'space' | 'pairs' | 'variables' | 'shares';
|
|
5
5
|
|
|
6
6
|
const PAIRS_MINIMUM_AXES = 3;
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* How many axes a cloud needs, which is one per direction of the box.
|
|
10
|
+
*
|
|
11
|
+
* A run that produced two is not shown a third empty axis: the cloud would be
|
|
12
|
+
* a flat map stood on its edge, which says less than the map it came from.
|
|
13
|
+
*/
|
|
14
|
+
const SPACE_MINIMUM_AXES = 3;
|
|
15
|
+
|
|
8
16
|
/**
|
|
9
17
|
* The tabs a result can actually fill.
|
|
10
18
|
*
|
|
@@ -32,6 +40,7 @@ export function projectionTabs(
|
|
|
32
40
|
const tabs: ProjectionTab[] = ['map'];
|
|
33
41
|
const enoughAxes = result.axes.length >= PAIRS_MINIMUM_AXES;
|
|
34
42
|
|
|
43
|
+
if (result.axes.length >= SPACE_MINIMUM_AXES) tabs.push('space');
|
|
35
44
|
if (enoughAxes) tabs.push('pairs');
|
|
36
45
|
if (result.loadings !== undefined) tabs.push('variables');
|
|
37
46
|
if (enoughAxes && everyAxisPublishesAShare(result.axes)) tabs.push('shares');
|
|
@@ -3,6 +3,7 @@ import { useMemo, useState } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import type { ChartViewport } from '../../chart/core/chartViewport.ts';
|
|
5
5
|
import { ScatterPlot } from '../../scatter/ui/ScatterPlot.tsx';
|
|
6
|
+
import type { ScatterPointOpen } from '../../scatter/ui/scatterPlotProps.ts';
|
|
6
7
|
import type { SelectionChange } from '../../scatter/ui/useScatterSelection.ts';
|
|
7
8
|
import type { ProjectionCopy } from '../core/projectionCopy.ts';
|
|
8
9
|
import type { ProjectionOptions } from '../core/projectionOptions.ts';
|
|
@@ -82,6 +83,11 @@ export interface ProjectionMapTabProps {
|
|
|
82
83
|
* @default undefined
|
|
83
84
|
*/
|
|
84
85
|
onHoverChange?: (index: number) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Called when the reader double-clicks a sample.
|
|
88
|
+
* @default undefined
|
|
89
|
+
*/
|
|
90
|
+
onPointDoubleClick?: (point: ScatterPointOpen) => void;
|
|
85
91
|
/**
|
|
86
92
|
* The card that follows the pointer. The viewer builds it, because it has to
|
|
87
93
|
* say the same thing on every tab.
|
|
@@ -121,6 +127,7 @@ export function ProjectionMapTab(props: ProjectionMapTabProps): ReactElement {
|
|
|
121
127
|
const { result, groups, options, copy, chrome, width, height } = props;
|
|
122
128
|
const { ids, selected, viewport = null, report = null } = props;
|
|
123
129
|
const { onSelectionChange, onHoverChange, hoverCard } = props;
|
|
130
|
+
const { onPointDoubleClick } = props;
|
|
124
131
|
const { onViewportChange, wheelZoom, touchLasso, testId } = props;
|
|
125
132
|
const { scores, fittedCount, markers } = result;
|
|
126
133
|
const { xAxis, yAxis, colorBy, showGroupMeans, pointRadius } = options;
|
|
@@ -173,6 +180,7 @@ export function ProjectionMapTab(props: ProjectionMapTabProps): ReactElement {
|
|
|
173
180
|
onSelectionChange={onSelectionChange}
|
|
174
181
|
selectMode={selectMode}
|
|
175
182
|
onHoverChange={onHoverChange}
|
|
183
|
+
onPointDoubleClick={onPointDoubleClick}
|
|
176
184
|
onLassoChange={setDrawing}
|
|
177
185
|
touchLasso={touchLasso}
|
|
178
186
|
testId={testId}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PointerEvent, ReactElement } from 'react';
|
|
2
2
|
import { useRef, useState } from 'react';
|
|
3
3
|
|
|
4
|
+
import type { ScatterPointOpen } from '../../scatter/ui/scatterPlotProps.ts';
|
|
4
5
|
import type { ProjectionCopy } from '../core/projectionCopy.ts';
|
|
5
6
|
import type { ProjectionResult } from '../core/projectionResult.ts';
|
|
6
7
|
import type { ProjectionSamples } from '../core/projectionSamples.ts';
|
|
@@ -9,10 +10,12 @@ import { ProjectionMapTab } from './ProjectionMapTab.tsx';
|
|
|
9
10
|
import { ProjectionPairsTab } from './ProjectionPairsTab.tsx';
|
|
10
11
|
import { ProjectionReadout } from './ProjectionReadout.tsx';
|
|
11
12
|
import { ProjectionSharesTab } from './ProjectionSharesTab.tsx';
|
|
13
|
+
import { ProjectionSpaceTab } from './ProjectionSpaceTab.tsx';
|
|
12
14
|
import type { ProjectionVariableTrack } from './ProjectionVariablesTab.tsx';
|
|
13
15
|
import { ProjectionVariablesTab } from './ProjectionVariablesTab.tsx';
|
|
14
16
|
import type { ProjectionMapView } from './projectionMapView.ts';
|
|
15
17
|
import type { ProjectionModels } from './projectionTabModels.ts';
|
|
18
|
+
import type { ProjectionSampleOpen } from './projectionViewerProps.ts';
|
|
16
19
|
import type { ProjectionStateApi } from './useProjectionState.ts';
|
|
17
20
|
|
|
18
21
|
/** What {@link ProjectionPanel} needs. */
|
|
@@ -46,6 +49,11 @@ export interface ProjectionPanelProps {
|
|
|
46
49
|
* @default undefined
|
|
47
50
|
*/
|
|
48
51
|
onHoverSample?: (id: string | null) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Called when the reader double-clicks a sample, on either figure.
|
|
54
|
+
* @default undefined
|
|
55
|
+
*/
|
|
56
|
+
onSampleDoubleClick?: (sample: ProjectionSampleOpen) => void;
|
|
49
57
|
/**
|
|
50
58
|
* Called as the pointer crosses the "what differs" panels.
|
|
51
59
|
* @default undefined
|
|
@@ -83,6 +91,7 @@ export function ProjectionPanel(props: ProjectionPanelProps): ReactElement {
|
|
|
83
91
|
const { state, result, samples, copy, models, map } = props;
|
|
84
92
|
const { width, height, formatValue, baseId, panelId, strip } = props;
|
|
85
93
|
const { onHoverSample, onTrackVariable, touchLasso, wheelZoom } = props;
|
|
94
|
+
const { onSampleDoubleClick } = props;
|
|
86
95
|
|
|
87
96
|
const box = useRef<HTMLDivElement>(null);
|
|
88
97
|
const pointer = useRef(NOWHERE);
|
|
@@ -102,6 +111,19 @@ export function ProjectionPanel(props: ProjectionPanelProps): ReactElement {
|
|
|
102
111
|
onHoverSample?.(index < 0 ? null : (samples.ids[index] ?? null));
|
|
103
112
|
}
|
|
104
113
|
|
|
114
|
+
// The figures speak in row numbers, because that is what a hit test hands
|
|
115
|
+
// back; every callback out of the viewer speaks in names, so a caller never
|
|
116
|
+
// has to keep a second copy of the row order. The translation is one line
|
|
117
|
+
// and it belongs here, once, rather than in each of the two tabs.
|
|
118
|
+
const openSample =
|
|
119
|
+
onSampleDoubleClick === undefined
|
|
120
|
+
? undefined
|
|
121
|
+
: (point: ScatterPointOpen) => {
|
|
122
|
+
const id = samples.ids[point.index];
|
|
123
|
+
if (id === undefined) return;
|
|
124
|
+
onSampleDoubleClick({ ...point, id });
|
|
125
|
+
};
|
|
126
|
+
|
|
105
127
|
return (
|
|
106
128
|
<div
|
|
107
129
|
ref={box}
|
|
@@ -143,6 +165,46 @@ export function ProjectionPanel(props: ProjectionPanelProps): ReactElement {
|
|
|
143
165
|
height={height}
|
|
144
166
|
/>
|
|
145
167
|
) : null}
|
|
168
|
+
{state.tab === 'space' ? (
|
|
169
|
+
<ProjectionSpaceTab
|
|
170
|
+
result={result}
|
|
171
|
+
groups={state.groups}
|
|
172
|
+
options={state.options}
|
|
173
|
+
copy={copy}
|
|
174
|
+
chrome={map.spaceChrome}
|
|
175
|
+
ids={samples.ids}
|
|
176
|
+
width={width}
|
|
177
|
+
height={height}
|
|
178
|
+
selected={state.selected}
|
|
179
|
+
camera={map.camera}
|
|
180
|
+
onCameraChange={map.setCamera}
|
|
181
|
+
zoom={map.zoom}
|
|
182
|
+
onZoomChange={map.setZoom}
|
|
183
|
+
wheelZoom={wheelZoom}
|
|
184
|
+
report={map.report}
|
|
185
|
+
onSelectionChange={map.settle}
|
|
186
|
+
onHoverChange={noteHover}
|
|
187
|
+
onPointDoubleClick={openSample}
|
|
188
|
+
hoverCard={
|
|
189
|
+
anchor.index < 0 ? null : (
|
|
190
|
+
<ProjectionReadout
|
|
191
|
+
index={anchor.index}
|
|
192
|
+
result={result}
|
|
193
|
+
samples={samples}
|
|
194
|
+
groups={state.groups}
|
|
195
|
+
xAxis={state.options.xAxis}
|
|
196
|
+
yAxis={state.options.yAxis}
|
|
197
|
+
zAxis={state.options.zAxis}
|
|
198
|
+
x={anchor.x}
|
|
199
|
+
y={anchor.y}
|
|
200
|
+
boxWidth={width}
|
|
201
|
+
boxHeight={height}
|
|
202
|
+
formatValue={formatValue}
|
|
203
|
+
/>
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
/>
|
|
207
|
+
) : null}
|
|
146
208
|
{state.tab === 'map' ? (
|
|
147
209
|
<ProjectionMapTab
|
|
148
210
|
result={result}
|
|
@@ -160,6 +222,7 @@ export function ProjectionPanel(props: ProjectionPanelProps): ReactElement {
|
|
|
160
222
|
selected={state.selected}
|
|
161
223
|
onSelectionChange={map.settle}
|
|
162
224
|
onHoverChange={noteHover}
|
|
225
|
+
onPointDoubleClick={openSample}
|
|
163
226
|
touchLasso={touchLasso}
|
|
164
227
|
hoverCard={
|
|
165
228
|
anchor.index < 0 ? null : (
|
|
@@ -25,6 +25,13 @@ export interface ProjectionReadoutProps {
|
|
|
25
25
|
xAxis: number;
|
|
26
26
|
/** Which axis is drawn vertically. */
|
|
27
27
|
yAxis: number;
|
|
28
|
+
/**
|
|
29
|
+
* Which axis runs away from the reader, on the cloud. It is the one place a
|
|
30
|
+
* cloud's third number is written at all: the box carries no tick labels, so
|
|
31
|
+
* a reader who wants a value points at the sample.
|
|
32
|
+
* @default undefined — the card names two axes, as the map's does
|
|
33
|
+
*/
|
|
34
|
+
zAxis?: number;
|
|
28
35
|
/** Where the pointer is, in pixels from the figure's left. */
|
|
29
36
|
x: number;
|
|
30
37
|
/** Where the pointer is, in pixels from the figure's top. */
|
|
@@ -80,6 +87,7 @@ export function ProjectionReadout(props: ProjectionReadoutProps): ReactElement {
|
|
|
80
87
|
groups,
|
|
81
88
|
xAxis,
|
|
82
89
|
yAxis,
|
|
90
|
+
zAxis,
|
|
83
91
|
x,
|
|
84
92
|
y,
|
|
85
93
|
boxWidth,
|
|
@@ -91,10 +99,13 @@ export function ProjectionReadout(props: ProjectionReadoutProps): ReactElement {
|
|
|
91
99
|
testId,
|
|
92
100
|
} = props;
|
|
93
101
|
|
|
102
|
+
const drawn = useMemo(
|
|
103
|
+
() => (zAxis === undefined ? [xAxis, yAxis] : [xAxis, yAxis, zAxis]),
|
|
104
|
+
[xAxis, yAxis, zAxis],
|
|
105
|
+
);
|
|
94
106
|
const rows = useMemo(
|
|
95
|
-
() =>
|
|
96
|
-
|
|
97
|
-
[formatValue, groups, index, result, samples, xAxis, yAxis],
|
|
107
|
+
() => readoutRows(index, result, samples, groups, drawn, formatValue),
|
|
108
|
+
[drawn, formatValue, groups, index, result, samples],
|
|
98
109
|
);
|
|
99
110
|
|
|
100
111
|
return (
|
|
@@ -118,15 +129,14 @@ function readoutRows(
|
|
|
118
129
|
result: ProjectionResult,
|
|
119
130
|
samples: ProjectionSamples,
|
|
120
131
|
groups: ResolvedProjectionGroups,
|
|
121
|
-
|
|
122
|
-
yAxis: number,
|
|
132
|
+
drawn: readonly number[],
|
|
123
133
|
formatValue: (value: number) => string,
|
|
124
134
|
): OverlayReadoutRow[] {
|
|
125
135
|
const rows: OverlayReadoutRow[] = [];
|
|
126
136
|
const { axes, scores } = result;
|
|
127
137
|
const known = index >= 0 && index < scores.rows;
|
|
128
138
|
|
|
129
|
-
for (const axis of
|
|
139
|
+
for (const axis of drawn) {
|
|
130
140
|
const name = axes[axis]?.name;
|
|
131
141
|
if (name === undefined) continue;
|
|
132
142
|
rows.push({
|