react-cheminfo 0.21.0 → 0.23.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 +14 -4
- package/lib/build/vite/gitHead.d.ts.map +1 -1
- package/lib/build/vite/gitHead.js +17 -2
- package/lib/build/vite/gitHead.js.map +1 -1
- package/lib/clipboard/ui/ClickToCopy.d.ts +69 -0
- package/lib/clipboard/ui/ClickToCopy.d.ts.map +1 -0
- package/lib/clipboard/ui/ClickToCopy.js +66 -0
- package/lib/clipboard/ui/ClickToCopy.js.map +1 -0
- package/lib/clipboard/ui/CopyButton.d.ts +1 -1
- package/lib/clipboard/ui/CopyButton.js +1 -1
- package/lib/clipboard/ui/CopyableValue.d.ts +3 -3
- package/lib/clipboard/ui/CopyableValue.d.ts.map +1 -1
- package/lib/clipboard/ui/CopyableValue.js +5 -5
- package/lib/clipboard/ui/CopyableValue.js.map +1 -1
- package/lib/clipboard/ui/index.d.ts +2 -0
- package/lib/clipboard/ui/index.d.ts.map +1 -1
- package/lib/clipboard/ui/index.js +1 -0
- package/lib/clipboard/ui/index.js.map +1 -1
- package/lib/ecosystem/core/groups.d.ts +4 -3
- package/lib/ecosystem/core/groups.d.ts.map +1 -1
- package/lib/ecosystem/core/groups.js +8 -2
- package/lib/ecosystem/core/groups.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +39 -0
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/core/types.d.ts +1 -1
- package/lib/ecosystem/core/types.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +9 -0
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/error/ui/ErrorFallback.d.ts.map +1 -1
- package/lib/error/ui/ErrorFallback.js +1 -1
- package/lib/error/ui/ErrorFallback.js.map +1 -1
- package/lib/overlay/core/index.d.ts +2 -2
- package/lib/overlay/core/index.d.ts.map +1 -1
- package/lib/overlay/core/index.js +1 -1
- package/lib/overlay/core/index.js.map +1 -1
- package/lib/overlay/core/overlayMarks.d.ts +20 -5
- package/lib/overlay/core/overlayMarks.d.ts.map +1 -1
- package/lib/overlay/core/overlayMarks.js +31 -5
- package/lib/overlay/core/overlayMarks.js.map +1 -1
- package/lib/overlay/ui/OverlayLegendMark.js +5 -0
- package/lib/overlay/ui/OverlayLegendMark.js.map +1 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.d.ts.map +1 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.js +2 -1
- package/lib/pedagogy/ui/ReferenceSectionBlock.js.map +1 -1
- package/lib/projection/core/index.d.ts +4 -4
- package/lib/projection/core/index.d.ts.map +1 -1
- package/lib/projection/core/index.js +2 -2
- package/lib/projection/core/index.js.map +1 -1
- package/lib/projection/core/projectionCopy.d.ts +2 -0
- package/lib/projection/core/projectionCopy.d.ts.map +1 -1
- package/lib/projection/core/projectionCopy.js +1 -0
- package/lib/projection/core/projectionCopy.js.map +1 -1
- package/lib/projection/core/projectionGroupings.d.ts +44 -0
- package/lib/projection/core/projectionGroupings.d.ts.map +1 -0
- package/lib/projection/core/projectionGroupings.js +132 -0
- package/lib/projection/core/projectionGroupings.js.map +1 -0
- package/lib/projection/core/projectionOptions.d.ts +16 -6
- package/lib/projection/core/projectionOptions.d.ts.map +1 -1
- package/lib/projection/core/projectionOptions.js +7 -1
- package/lib/projection/core/projectionOptions.js.map +1 -1
- package/lib/projection/core/projectionPanelWords.d.ts +6 -0
- package/lib/projection/core/projectionPanelWords.d.ts.map +1 -1
- package/lib/projection/core/projectionPanelWords.js +4 -0
- package/lib/projection/core/projectionPanelWords.js.map +1 -1
- package/lib/projection/core/projectionSamples.d.ts +63 -32
- package/lib/projection/core/projectionSamples.d.ts.map +1 -1
- package/lib/projection/core/projectionSamples.js +1 -80
- package/lib/projection/core/projectionSamples.js.map +1 -1
- package/lib/projection/core/projectionStrings.d.ts +2 -0
- package/lib/projection/core/projectionStrings.d.ts.map +1 -1
- package/lib/projection/core/projectionStrings.js +10 -1
- package/lib/projection/core/projectionStrings.js.map +1 -1
- package/lib/projection/core/resolveProjectionOptions.d.ts +8 -3
- package/lib/projection/core/resolveProjectionOptions.d.ts.map +1 -1
- package/lib/projection/core/resolveProjectionOptions.js +58 -5
- package/lib/projection/core/resolveProjectionOptions.js.map +1 -1
- package/lib/projection/ui/ProjectionBar.d.ts +1 -1
- package/lib/projection/ui/ProjectionBar.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionBar.js +3 -1
- package/lib/projection/ui/ProjectionBar.js.map +1 -1
- package/lib/projection/ui/ProjectionGroupingPickers.d.ts +38 -0
- package/lib/projection/ui/ProjectionGroupingPickers.d.ts.map +1 -0
- package/lib/projection/ui/ProjectionGroupingPickers.js +33 -0
- package/lib/projection/ui/ProjectionGroupingPickers.js.map +1 -0
- package/lib/projection/ui/ProjectionMapControls.d.ts +6 -7
- package/lib/projection/ui/ProjectionMapControls.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionMapControls.js +11 -9
- package/lib/projection/ui/ProjectionMapControls.js.map +1 -1
- package/lib/projection/ui/ProjectionMapLegend.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionMapLegend.js +1 -1
- package/lib/projection/ui/ProjectionMapLegend.js.map +1 -1
- package/lib/projection/ui/ProjectionMapMore.d.ts +2 -2
- package/lib/projection/ui/ProjectionMapMore.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionMapMore.js +7 -5
- package/lib/projection/ui/ProjectionMapMore.js.map +1 -1
- package/lib/projection/ui/ProjectionMapTab.d.ts +15 -6
- package/lib/projection/ui/ProjectionMapTab.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionMapTab.js +11 -7
- package/lib/projection/ui/ProjectionMapTab.js.map +1 -1
- package/lib/projection/ui/ProjectionPairsMore.d.ts +3 -7
- package/lib/projection/ui/ProjectionPairsMore.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionPairsMore.js +4 -5
- package/lib/projection/ui/ProjectionPairsMore.js.map +1 -1
- package/lib/projection/ui/ProjectionPairsTab.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionPairsTab.js +2 -2
- package/lib/projection/ui/ProjectionPairsTab.js.map +1 -1
- package/lib/projection/ui/ProjectionPanel.d.ts +1 -1
- package/lib/projection/ui/ProjectionPanel.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionPanel.js +1 -1
- package/lib/projection/ui/ProjectionPanel.js.map +1 -1
- package/lib/projection/ui/ProjectionReadout.d.ts +7 -1
- package/lib/projection/ui/ProjectionReadout.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionReadout.js +19 -7
- package/lib/projection/ui/ProjectionReadout.js.map +1 -1
- package/lib/projection/ui/ProjectionSpaceControls.d.ts +3 -5
- package/lib/projection/ui/ProjectionSpaceControls.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionSpaceControls.js +9 -7
- package/lib/projection/ui/ProjectionSpaceControls.js.map +1 -1
- package/lib/projection/ui/ProjectionSpaceMore.d.ts +2 -2
- package/lib/projection/ui/ProjectionSpaceMore.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionSpaceMore.js +5 -4
- package/lib/projection/ui/ProjectionSpaceMore.js.map +1 -1
- package/lib/projection/ui/ProjectionSpaceTab.d.ts +11 -3
- package/lib/projection/ui/ProjectionSpaceTab.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionSpaceTab.js +7 -4
- package/lib/projection/ui/ProjectionSpaceTab.js.map +1 -1
- package/lib/projection/ui/ProjectionViewer.d.ts.map +1 -1
- package/lib/projection/ui/ProjectionViewer.js +1 -0
- package/lib/projection/ui/ProjectionViewer.js.map +1 -1
- package/lib/projection/ui/projectionBarReadings.d.ts +4 -1
- package/lib/projection/ui/projectionBarReadings.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarReadings.js +14 -4
- package/lib/projection/ui/projectionBarReadings.js.map +1 -1
- package/lib/projection/ui/projectionBarSlots.d.ts +4 -2
- package/lib/projection/ui/projectionBarSlots.d.ts.map +1 -1
- package/lib/projection/ui/projectionBarSlots.js +3 -7
- package/lib/projection/ui/projectionBarSlots.js.map +1 -1
- package/lib/projection/ui/projectionMapChoices.d.ts +31 -6
- package/lib/projection/ui/projectionMapChoices.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapChoices.js +58 -9
- package/lib/projection/ui/projectionMapChoices.js.map +1 -1
- package/lib/projection/ui/projectionMapChrome.d.ts +3 -9
- package/lib/projection/ui/projectionMapChrome.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapChrome.js +41 -27
- package/lib/projection/ui/projectionMapChrome.js.map +1 -1
- package/lib/projection/ui/projectionMapCorner.d.ts +25 -0
- package/lib/projection/ui/projectionMapCorner.d.ts.map +1 -0
- package/lib/projection/ui/projectionMapCorner.js +46 -0
- package/lib/projection/ui/projectionMapCorner.js.map +1 -0
- package/lib/projection/ui/projectionMapModel.d.ts +9 -18
- package/lib/projection/ui/projectionMapModel.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapModel.js +14 -39
- package/lib/projection/ui/projectionMapModel.js.map +1 -1
- package/lib/projection/ui/projectionMapView.d.ts +3 -1
- package/lib/projection/ui/projectionMapView.d.ts.map +1 -1
- package/lib/projection/ui/projectionMapView.js +9 -4
- package/lib/projection/ui/projectionMapView.js.map +1 -1
- package/lib/projection/ui/projectionSelectionSentence.d.ts +10 -0
- package/lib/projection/ui/projectionSelectionSentence.d.ts.map +1 -0
- package/lib/projection/ui/projectionSelectionSentence.js +18 -0
- package/lib/projection/ui/projectionSelectionSentence.js.map +1 -0
- package/lib/projection/ui/projectionStateApi.d.ts +47 -0
- package/lib/projection/ui/projectionStateApi.d.ts.map +1 -0
- package/lib/projection/ui/projectionStateApi.js +9 -0
- package/lib/projection/ui/projectionStateApi.js.map +1 -0
- package/lib/projection/ui/projectionTabModels.js +1 -1
- package/lib/projection/ui/projectionTabModels.js.map +1 -1
- package/lib/projection/ui/useProjectionState.d.ts +1 -29
- package/lib/projection/ui/useProjectionState.d.ts.map +1 -1
- package/lib/projection/ui/useProjectionState.js +13 -5
- package/lib/projection/ui/useProjectionState.js.map +1 -1
- package/lib/scatter/core/index.d.ts +1 -0
- package/lib/scatter/core/index.d.ts.map +1 -1
- package/lib/scatter/core/index.js +1 -0
- package/lib/scatter/core/index.js.map +1 -1
- package/lib/scatter/core/pointShape.d.ts +18 -0
- package/lib/scatter/core/pointShape.d.ts.map +1 -0
- package/lib/scatter/core/pointShape.js +70 -0
- package/lib/scatter/core/pointShape.js.map +1 -0
- package/lib/scatter/ui/ScatterPlot.d.ts.map +1 -1
- package/lib/scatter/ui/ScatterPlot.js +3 -2
- package/lib/scatter/ui/ScatterPlot.js.map +1 -1
- package/lib/scatter/ui/ScatterPointLayer.d.ts +14 -1
- package/lib/scatter/ui/ScatterPointLayer.d.ts.map +1 -1
- package/lib/scatter/ui/ScatterPointLayer.js +15 -3
- package/lib/scatter/ui/ScatterPointLayer.js.map +1 -1
- package/lib/scatter/ui/scatterFigureProps.d.ts +13 -0
- package/lib/scatter/ui/scatterFigureProps.d.ts.map +1 -1
- package/lib/scatter/ui/scatterMarkGlyph.d.ts +2 -1
- package/lib/scatter/ui/scatterMarkGlyph.d.ts.map +1 -1
- package/lib/scatter/ui/scatterMarkGlyph.js +7 -1
- package/lib/scatter/ui/scatterMarkGlyph.js.map +1 -1
- package/lib/scatter3d/ui/ScatterCloud.d.ts.map +1 -1
- package/lib/scatter3d/ui/ScatterCloud.js +2 -1
- package/lib/scatter3d/ui/ScatterCloud.js.map +1 -1
- package/package.json +1 -1
- package/src/build/vite/gitHead.ts +17 -2
- package/src/clipboard/ui/ClickToCopy.tsx +190 -0
- package/src/clipboard/ui/CopyButton.tsx +2 -2
- package/src/clipboard/ui/CopyableValue.tsx +11 -6
- package/src/clipboard/ui/index.ts +2 -0
- package/src/ecosystem/core/groups.ts +10 -3
- package/src/ecosystem/core/sites.ts +39 -0
- package/src/ecosystem/core/types.ts +4 -1
- package/src/ecosystem/ui/glyphs.tsx +51 -0
- package/src/error/ui/ErrorFallback.tsx +3 -1
- package/src/overlay/core/index.ts +6 -2
- package/src/overlay/core/overlayMarks.ts +51 -6
- package/src/overlay/ui/OverlayLegendMark.tsx +5 -0
- package/src/pedagogy/ui/ReferenceSectionBlock.tsx +10 -1
- package/src/projection/core/index.ts +11 -3
- package/src/projection/core/projectionCopy.ts +3 -0
- package/src/projection/core/projectionGroupings.ts +174 -0
- package/src/projection/core/projectionOptions.ts +18 -7
- package/src/projection/core/projectionPanelWords.ts +10 -0
- package/src/projection/core/projectionSamples.ts +62 -120
- package/src/projection/core/projectionStrings.ts +12 -1
- package/src/projection/core/resolveProjectionOptions.ts +66 -4
- package/src/projection/ui/ProjectionBar.tsx +4 -2
- package/src/projection/ui/ProjectionGroupingPickers.tsx +91 -0
- package/src/projection/ui/ProjectionMapControls.tsx +29 -32
- package/src/projection/ui/ProjectionMapLegend.tsx +1 -1
- package/src/projection/ui/ProjectionMapMore.tsx +14 -4
- package/src/projection/ui/ProjectionMapTab.tsx +27 -9
- package/src/projection/ui/ProjectionPairsMore.tsx +12 -21
- package/src/projection/ui/ProjectionPairsTab.tsx +5 -2
- package/src/projection/ui/ProjectionPanel.tsx +7 -3
- package/src/projection/ui/ProjectionReadout.tsx +28 -8
- package/src/projection/ui/ProjectionSpaceControls.tsx +20 -27
- package/src/projection/ui/ProjectionSpaceMore.tsx +12 -3
- package/src/projection/ui/ProjectionSpaceTab.tsx +23 -6
- package/src/projection/ui/ProjectionViewer.tsx +1 -0
- package/src/projection/ui/projectionBarReadings.ts +24 -8
- package/src/projection/ui/projectionBarSlots.tsx +10 -9
- package/src/projection/ui/projectionMapChoices.ts +71 -14
- package/src/projection/ui/projectionMapChrome.ts +55 -30
- package/src/projection/ui/projectionMapCorner.ts +67 -0
- package/src/projection/ui/projectionMapModel.ts +20 -59
- package/src/projection/ui/projectionMapView.ts +15 -8
- package/src/projection/ui/projectionSelectionSentence.ts +22 -0
- package/src/projection/ui/projectionStateApi.ts +57 -0
- package/src/projection/ui/projectionTabModels.ts +1 -1
- package/src/projection/ui/useProjectionState.ts +33 -37
- package/src/scatter/core/index.ts +1 -0
- package/src/scatter/core/pointShape.ts +80 -0
- package/src/scatter/ui/ScatterPlot.tsx +4 -1
- package/src/scatter/ui/ScatterPointLayer.tsx +39 -11
- package/src/scatter/ui/scatterFigureProps.ts +13 -0
- package/src/scatter/ui/scatterMarkGlyph.tsx +9 -1
- package/src/scatter3d/ui/ScatterCloud.tsx +3 -0
- package/styles/chrome.css +163 -0
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CHART_SERIES_COLORS,
|
|
3
|
-
chartSeriesColor,
|
|
4
|
-
} from '../../chart/core/chartPalette.ts';
|
|
1
|
+
import type { OverlaySampleShape } from '../../overlay/core/overlayMarks.ts';
|
|
5
2
|
|
|
6
3
|
/** One line of the card that appears when the pointer rests on a sample. */
|
|
7
4
|
export interface ProjectionField {
|
|
@@ -11,46 +8,72 @@ export interface ProjectionField {
|
|
|
11
8
|
value: string;
|
|
12
9
|
}
|
|
13
10
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
11
|
+
/**
|
|
12
|
+
* One way of sorting the rows into groups — a species, a batch, the cluster a
|
|
13
|
+
* run put each sample in.
|
|
14
|
+
*/
|
|
15
|
+
export interface ProjectionGrouping {
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* What the options name it by, e.g. `species`. It is kept apart from the
|
|
18
|
+
* label so that a saved choice still finds the grouping once its label is
|
|
19
|
+
* reworded, and it may not be `none`, which is the word for no grouping.
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
+
id: string;
|
|
22
|
+
/** What the set of groups is called, for the key and the hover card: `Species`. */
|
|
23
|
+
label: string;
|
|
21
24
|
/**
|
|
22
|
-
*
|
|
25
|
+
* Which group each row is in, in the score matrix's row order. A row with no
|
|
23
26
|
* group is drawn in the muted ink and left out of every outline.
|
|
24
|
-
* @default undefined — every row is one crowd
|
|
25
27
|
*/
|
|
26
|
-
groups
|
|
28
|
+
groups: ReadonlyArray<string | undefined>;
|
|
27
29
|
/**
|
|
28
|
-
* The order the groups are listed and
|
|
30
|
+
* The order the groups are listed, coloured and shaped in.
|
|
29
31
|
* @default the order the groups first appear in `groups`
|
|
30
32
|
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* What the set of groups is called, for the legend's heading and the hover
|
|
34
|
-
* card — `Species`, `Cluster`, `Batch`.
|
|
35
|
-
* @default 'Group'
|
|
36
|
-
*/
|
|
37
|
-
groupLabel?: string;
|
|
33
|
+
order?: readonly string[];
|
|
38
34
|
/**
|
|
39
35
|
* A colour per group. Anything not named here takes the next unused colour
|
|
40
36
|
* of the shared palette, so two groups never share one.
|
|
41
37
|
* @default undefined — every group is coloured from the palette
|
|
42
38
|
*/
|
|
43
|
-
|
|
39
|
+
colors?: Readonly<Record<string, string>>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Who the rows are: what they are called, what they belong to, what is known. */
|
|
43
|
+
export interface ProjectionSamples {
|
|
44
|
+
/**
|
|
45
|
+
* A stable key per row, in the score matrix's row order. It is the currency
|
|
46
|
+
* of the selection callbacks, so a caller never has to keep a second index.
|
|
47
|
+
*/
|
|
48
|
+
ids: readonly string[];
|
|
49
|
+
/**
|
|
50
|
+
* What each row is written as: the title of its hover card and the name
|
|
51
|
+
* beside its dot. A page whose keys are not names a reader should read — a
|
|
52
|
+
* database id, a spectrum's uuid — writes its names here and keeps `ids`
|
|
53
|
+
* stable, and two samples may then share a name without either becoming
|
|
54
|
+
* impossible to select.
|
|
55
|
+
* @default the ids
|
|
56
|
+
*/
|
|
57
|
+
labels?: readonly string[];
|
|
58
|
+
/**
|
|
59
|
+
* The ways the rows are grouped. The first colours the dots until the reader
|
|
60
|
+
* picks another, and the next one gives them their shapes, so two groupings
|
|
61
|
+
* of the same samples — the clusters a run found and the classes the reader
|
|
62
|
+
* gave — are compared on one picture. Every one of them is written on the
|
|
63
|
+
* hover card.
|
|
64
|
+
* @default undefined — every row is one crowd
|
|
65
|
+
*/
|
|
66
|
+
groupings?: readonly ProjectionGrouping[];
|
|
44
67
|
/**
|
|
45
68
|
* Everything else worth showing about one row. A callback rather than an
|
|
46
69
|
* array, so a table of ten thousand rows builds one record when a reader
|
|
47
70
|
* points at one row, and none otherwise.
|
|
48
|
-
* @default undefined — the card shows the id, the
|
|
71
|
+
* @default undefined — the card shows the id, the groups and the two axes
|
|
49
72
|
*/
|
|
50
73
|
fields?: (index: number) => readonly ProjectionField[];
|
|
51
74
|
}
|
|
52
75
|
|
|
53
|
-
/** The
|
|
76
|
+
/** The grouping that colours the dots, as the viewer draws it. */
|
|
54
77
|
export interface ResolvedProjectionGroups {
|
|
55
78
|
/** What the set of groups is called. */
|
|
56
79
|
label: string;
|
|
@@ -69,102 +92,21 @@ export interface ResolvedProjectionGroups {
|
|
|
69
92
|
groupOf: Int32Array;
|
|
70
93
|
}
|
|
71
94
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
interface GroupEntry {
|
|
76
|
-
id: string;
|
|
95
|
+
/** The grouping that shapes the dots, as the viewer draws it. */
|
|
96
|
+
export interface ResolvedProjectionShapes {
|
|
97
|
+
/** What the set of groups is called. */
|
|
77
98
|
label: string;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
* @returns The resolved groups. A set of samples with no `groups` gives no
|
|
92
|
-
* entries at all, and every row lands at `-1`.
|
|
93
|
-
*/
|
|
94
|
-
export function resolveProjectionGroups(
|
|
95
|
-
samples: ProjectionSamples,
|
|
96
|
-
count: number,
|
|
97
|
-
): ResolvedProjectionGroups {
|
|
98
|
-
const rowCount = Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
|
|
99
|
-
const label = samples.groupLabel ?? DEFAULT_GROUP_LABEL;
|
|
100
|
-
const groupOf = new Int32Array(rowCount).fill(NO_GROUP);
|
|
101
|
-
const { groups, groupColors, groupOrder } = samples;
|
|
102
|
-
if (groups === undefined) return { label, entries: [], groupOf };
|
|
103
|
-
|
|
104
|
-
const entries: GroupEntry[] = [];
|
|
105
|
-
const indexOfGroup = new Map<string, number>();
|
|
106
|
-
if (groupOrder !== undefined) {
|
|
107
|
-
for (const id of groupOrder) appendGroup(entries, indexOfGroup, id);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
for (let row = 0; row < rowCount; row++) {
|
|
111
|
-
const id = groups[row];
|
|
112
|
-
if (id === undefined) continue;
|
|
113
|
-
const at = appendGroup(entries, indexOfGroup, id);
|
|
114
|
-
groupOf[row] = at;
|
|
115
|
-
const entry = entries[at];
|
|
116
|
-
if (entry !== undefined) entry.count++;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
paintGroups(entries, groupColors);
|
|
120
|
-
return { label, entries, groupOf };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function appendGroup(
|
|
124
|
-
entries: GroupEntry[],
|
|
125
|
-
indexOfGroup: Map<string, number>,
|
|
126
|
-
id: string,
|
|
127
|
-
): number {
|
|
128
|
-
const known = indexOfGroup.get(id);
|
|
129
|
-
if (known !== undefined) return known;
|
|
130
|
-
const at = entries.length;
|
|
131
|
-
indexOfGroup.set(id, at);
|
|
132
|
-
entries.push({ id, label: id, color: '', count: 0 });
|
|
133
|
-
return at;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function paintGroups(
|
|
137
|
-
entries: GroupEntry[],
|
|
138
|
-
pinned: Readonly<Record<string, string>> | undefined,
|
|
139
|
-
): void {
|
|
140
|
-
// Every pinned colour is taken out of the palette before any group draws
|
|
141
|
-
// from it, so pinning the third group's hue onto the first does not leave
|
|
142
|
-
// two groups wearing it.
|
|
143
|
-
const taken = new Set<string>();
|
|
144
|
-
if (pinned !== undefined) {
|
|
145
|
-
for (const entry of entries) {
|
|
146
|
-
const color = pinned[entry.id];
|
|
147
|
-
if (color !== undefined) taken.add(color.toLowerCase());
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
let cursor = 0;
|
|
152
|
-
for (const entry of entries) {
|
|
153
|
-
const own = pinned?.[entry.id];
|
|
154
|
-
if (own !== undefined) {
|
|
155
|
-
entry.color = own;
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
let color = chartSeriesColor(cursor, 'group');
|
|
159
|
-
while (
|
|
160
|
-
cursor < CHART_SERIES_COLORS.length &&
|
|
161
|
-
taken.has(color.toLowerCase())
|
|
162
|
-
) {
|
|
163
|
-
cursor++;
|
|
164
|
-
color = chartSeriesColor(cursor, 'group');
|
|
165
|
-
}
|
|
166
|
-
cursor++;
|
|
167
|
-
taken.add(color.toLowerCase());
|
|
168
|
-
entry.color = color;
|
|
169
|
-
}
|
|
99
|
+
/** The groups, in the order they are listed and given their shapes. */
|
|
100
|
+
entries: ReadonlyArray<{
|
|
101
|
+
/** The group's own name, which is also its id. */
|
|
102
|
+
id: string;
|
|
103
|
+
/** What it is called. */
|
|
104
|
+
label: string;
|
|
105
|
+
/** Its shape. */
|
|
106
|
+
shape: OverlaySampleShape;
|
|
107
|
+
/** How many rows are in it. */
|
|
108
|
+
count: number;
|
|
109
|
+
}>;
|
|
110
|
+
/** Which group each row is in, as an index into `entries`, or `-1`. */
|
|
111
|
+
shapeOf: Int32Array;
|
|
170
112
|
}
|
|
@@ -70,6 +70,14 @@ export const PROJECTION_HELP: Record<ProjectionOptionId, ProjectionHelp> = {
|
|
|
70
70
|
title: 'Colour by',
|
|
71
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.',
|
|
72
72
|
},
|
|
73
|
+
shapeBy: {
|
|
74
|
+
title: 'Shape by',
|
|
75
|
+
body: 'What a dot’s shape stands for. With colour for one grouping and shape for another, you see at a glance where two ways of sorting the same samples disagree — the clusters a run found against the classes you gave. Past five groups the shapes stop being told apart, so a grouping that large is not offered.',
|
|
76
|
+
example: {
|
|
77
|
+
code: 'Colour = cluster · Shape = class',
|
|
78
|
+
note: 'A square among circles of the same colour is a sample the clustering put with another class.',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
73
81
|
ellipse: {
|
|
74
82
|
title: 'Group outlines',
|
|
75
83
|
body: 'Each outline is drawn to cover about that share of its group’s samples, assuming the group is roughly bell-shaped. It is a summary of where a group sits, not a boundary.',
|
|
@@ -95,7 +103,7 @@ export const PROJECTION_HELP: Record<ProjectionOptionId, ProjectionHelp> = {
|
|
|
95
103
|
body: 'Writes every sample’s own name beside its dot. It is what turns a dot sitting on its own into something you can go and look up. In a crowd there is only room for some of the names, so the ones that do not fit are left out rather than written over each other.',
|
|
96
104
|
example: {
|
|
97
105
|
code: '0326_3a',
|
|
98
|
-
note: 'The name is
|
|
106
|
+
note: 'The name is the one the sample was loaded with, so it is the one you can find again in your own files.',
|
|
99
107
|
},
|
|
100
108
|
},
|
|
101
109
|
pairCount: {
|
|
@@ -189,6 +197,8 @@ export interface ProjectionBarWords {
|
|
|
189
197
|
};
|
|
190
198
|
/** What "the colour stands for nothing" reads. */
|
|
191
199
|
uncoloured: string;
|
|
200
|
+
/** What "the shape stands for nothing" reads. */
|
|
201
|
+
unshaped: string;
|
|
192
202
|
/** What "no outlines at all" reads where there is no room for a sentence. */
|
|
193
203
|
noOutlines: string;
|
|
194
204
|
/** What the settings gathered into one chip are called together. */
|
|
@@ -218,6 +228,7 @@ export const PROJECTION_BAR_WORDS: ProjectionBarWords = {
|
|
|
218
228
|
showIds: 'Show sample ID',
|
|
219
229
|
},
|
|
220
230
|
uncoloured: 'Nothing',
|
|
231
|
+
unshaped: 'Nothing',
|
|
221
232
|
noOutlines: 'None',
|
|
222
233
|
settings: 'How this is drawn',
|
|
223
234
|
view: {
|
|
@@ -8,8 +8,12 @@ import type {
|
|
|
8
8
|
ProjectionOptionId,
|
|
9
9
|
ProjectionOptions,
|
|
10
10
|
} from './projectionOptions.ts';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_PROJECTION_OPTIONS,
|
|
13
|
+
NO_GROUPING,
|
|
14
|
+
} from './projectionOptions.ts';
|
|
12
15
|
import type { ProjectionResult } from './projectionResult.ts';
|
|
16
|
+
import type { ProjectionGrouping } from './projectionSamples.ts';
|
|
13
17
|
import { drawableVariablesView } from './variablesView.ts';
|
|
14
18
|
|
|
15
19
|
/**
|
|
@@ -19,15 +23,20 @@ import { drawableVariablesView } from './variablesView.ts';
|
|
|
19
23
|
* A saved option outlives the data it was made on: reload with fewer samples
|
|
20
24
|
* and axis eight no longer exists. Rather than let a plot ask for a column
|
|
21
25
|
* that is not there, the two axes are pulled back inside what was computed and
|
|
22
|
-
* kept apart, the counts are held inside what exists,
|
|
23
|
-
* missing falls back to `weights
|
|
26
|
+
* kept apart, the counts are held inside what exists, a view whose data is
|
|
27
|
+
* missing falls back to `weights`, and the colour and the shape name groupings
|
|
28
|
+
* the samples actually carry.
|
|
24
29
|
* @param overrides - What the caller asked for.
|
|
25
30
|
* @param result - The reduced space the options will be applied to.
|
|
31
|
+
* @param groupings - The groupings the samples carry.
|
|
32
|
+
* @default undefined — the colour and the shape are left as they were asked
|
|
33
|
+
* for, since nothing says which groupings exist
|
|
26
34
|
* @returns Every option, complete and safe.
|
|
27
35
|
*/
|
|
28
36
|
export function resolveProjectionOptions(
|
|
29
37
|
overrides: Partial<ProjectionOptions> | undefined,
|
|
30
38
|
result: ProjectionResult,
|
|
39
|
+
groupings?: readonly ProjectionGrouping[],
|
|
31
40
|
): ProjectionOptions {
|
|
32
41
|
const axisCount = result.axes.length;
|
|
33
42
|
const lastAxis = Math.max(0, axisCount - 1);
|
|
@@ -36,6 +45,7 @@ export function resolveProjectionOptions(
|
|
|
36
45
|
pick(overrides, 'yAxis'),
|
|
37
46
|
axisCount,
|
|
38
47
|
);
|
|
48
|
+
const colorBy = colourChoice(pick(overrides, 'colorBy'), groupings);
|
|
39
49
|
|
|
40
50
|
return {
|
|
41
51
|
xAxis,
|
|
@@ -46,7 +56,8 @@ export function resolveProjectionOptions(
|
|
|
46
56
|
lastAxis,
|
|
47
57
|
),
|
|
48
58
|
cloudGesture: pick(overrides, 'cloudGesture'),
|
|
49
|
-
colorBy
|
|
59
|
+
colorBy,
|
|
60
|
+
shapeBy: shapeChoice(pick(overrides, 'shapeBy'), groupings, colorBy),
|
|
50
61
|
ellipse: pick(overrides, 'ellipse'),
|
|
51
62
|
pointRadius: atLeast(pick(overrides, 'pointRadius'), 3.5, 0),
|
|
52
63
|
showGroupMeans: pick(overrides, 'showGroupMeans'),
|
|
@@ -90,6 +101,57 @@ function pick<Key extends ProjectionOptionId>(
|
|
|
90
101
|
return value === undefined ? DEFAULT_PROJECTION_OPTIONS[key] : value;
|
|
91
102
|
}
|
|
92
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The grouping the colour will actually stand for.
|
|
106
|
+
*
|
|
107
|
+
* A saved choice outlives the data it was made on, so an id no grouping
|
|
108
|
+
* carries — or the empty string, which is how "the usual one" is asked for —
|
|
109
|
+
* falls to the first grouping, while `none` stays none.
|
|
110
|
+
* @param wanted - The id asked for.
|
|
111
|
+
* @param groupings - The groupings the samples carry.
|
|
112
|
+
* @returns A grouping's id, or `none`.
|
|
113
|
+
*/
|
|
114
|
+
function colourChoice(
|
|
115
|
+
wanted: string,
|
|
116
|
+
groupings: readonly ProjectionGrouping[] | undefined,
|
|
117
|
+
): string {
|
|
118
|
+
if (wanted === NO_GROUPING || groupings === undefined) return wanted;
|
|
119
|
+
const ids = groupingIds(groupings);
|
|
120
|
+
return ids.includes(wanted) ? wanted : (ids[0] ?? NO_GROUPING);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The grouping the shape will actually stand for.
|
|
125
|
+
*
|
|
126
|
+
* The usual one is the second grouping, since the first is the colour's; it is
|
|
127
|
+
* never read as "whichever the colour leaves free", or turning the colour off
|
|
128
|
+
* would suddenly give the dots shapes they did not have. Only when the colour
|
|
129
|
+
* has taken the second does the shape fall back to the first.
|
|
130
|
+
* @param wanted - The id asked for.
|
|
131
|
+
* @param groupings - The groupings the samples carry.
|
|
132
|
+
* @param colorBy - The grouping the colour already stands for.
|
|
133
|
+
* @returns A grouping's id, or `none`.
|
|
134
|
+
*/
|
|
135
|
+
function shapeChoice(
|
|
136
|
+
wanted: string,
|
|
137
|
+
groupings: readonly ProjectionGrouping[] | undefined,
|
|
138
|
+
colorBy: string,
|
|
139
|
+
): string {
|
|
140
|
+
if (wanted === NO_GROUPING || groupings === undefined) return wanted;
|
|
141
|
+
const ids = groupingIds(groupings);
|
|
142
|
+
if (wanted !== colorBy && ids.includes(wanted)) return wanted;
|
|
143
|
+
const [first, second] = ids;
|
|
144
|
+
if (second === undefined) return NO_GROUPING;
|
|
145
|
+
if (second !== colorBy) return second;
|
|
146
|
+
return first ?? NO_GROUPING;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function groupingIds(groupings: readonly ProjectionGrouping[]): string[] {
|
|
150
|
+
const ids: string[] = [];
|
|
151
|
+
for (const { id } of groupings) ids.push(id);
|
|
152
|
+
return ids;
|
|
153
|
+
}
|
|
154
|
+
|
|
93
155
|
/**
|
|
94
156
|
* An axis that is none of the ones already drawn.
|
|
95
157
|
*
|
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
projectionFoldedCaptionStyle,
|
|
28
28
|
} from './projectionBarStyles.ts';
|
|
29
29
|
import type { ProjectionMapView } from './projectionMapView.ts';
|
|
30
|
+
import type { ProjectionStateApi } from './projectionStateApi.ts';
|
|
30
31
|
import type { ProjectionModels } from './projectionTabModels.ts';
|
|
31
|
-
import type { ProjectionStateApi } from './useProjectionState.ts';
|
|
32
32
|
|
|
33
33
|
/** What {@link ProjectionBar} needs. */
|
|
34
34
|
interface ProjectionBarProps {
|
|
@@ -81,7 +81,8 @@ interface ProjectionBarProps {
|
|
|
81
81
|
export function ProjectionBar(props: ProjectionBarProps): ReactElement {
|
|
82
82
|
const { state, result, copy, models, map, baseId, panelId } = props;
|
|
83
83
|
const { fileName, width } = props;
|
|
84
|
-
const { tab, tabs, options, groups,
|
|
84
|
+
const { tab, tabs, options, groups, groupings, selected } = state;
|
|
85
|
+
const { setOptions, setTab } = state;
|
|
85
86
|
|
|
86
87
|
// Read here rather than off the surface below, because the row the bar is
|
|
87
88
|
// laid into is the surface's own parent: it has to know how tall a control
|
|
@@ -119,6 +120,7 @@ export function ProjectionBar(props: ProjectionBarProps): ReactElement {
|
|
|
119
120
|
map,
|
|
120
121
|
options,
|
|
121
122
|
groups,
|
|
123
|
+
groupings,
|
|
122
124
|
selected,
|
|
123
125
|
width,
|
|
124
126
|
readings,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import { OverlaySegmented } from '../../overlay/ui/OverlaySegmented.tsx';
|
|
4
|
+
import { OverlaySelect } from '../../overlay/ui/OverlaySelect.tsx';
|
|
5
|
+
import type { ProjectionCopy } from '../core/projectionCopy.ts';
|
|
6
|
+
import type { ProjectionOptions } from '../core/projectionOptions.ts';
|
|
7
|
+
import type { ProjectionGrouping } from '../core/projectionSamples.ts';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
projectionColourChoices,
|
|
11
|
+
projectionColourPatch,
|
|
12
|
+
projectionShapeChoices,
|
|
13
|
+
} from './projectionMapChoices.ts';
|
|
14
|
+
|
|
15
|
+
/** What a grouping picker needs. */
|
|
16
|
+
interface ProjectionGroupingPickerProps {
|
|
17
|
+
/** What the figure is showing, already resolved against the result. */
|
|
18
|
+
options: ProjectionOptions;
|
|
19
|
+
/** Called with only the options that changed. */
|
|
20
|
+
onChange: (patch: Partial<ProjectionOptions>) => void;
|
|
21
|
+
/** The words the viewer writes. */
|
|
22
|
+
copy: ProjectionCopy;
|
|
23
|
+
/** Every grouping the samples carry. */
|
|
24
|
+
groupings: readonly ProjectionGrouping[];
|
|
25
|
+
/** What the setting is called beside the control. */
|
|
26
|
+
label: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** What the shape picker needs on top of what every grouping picker takes. */
|
|
30
|
+
interface ProjectionShapePickerProps extends ProjectionGroupingPickerProps {
|
|
31
|
+
/** How many rows there are, which is what a grouping is cut over. */
|
|
32
|
+
rows: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* What the colour stands for, as segments while the choices fit on one row
|
|
37
|
+
* and as a list past that.
|
|
38
|
+
* @param props - See {@link ProjectionGroupingPickerProps}.
|
|
39
|
+
* @returns The picker, greyed while the samples carry no grouping.
|
|
40
|
+
*/
|
|
41
|
+
export function ProjectionColourPicker(
|
|
42
|
+
props: ProjectionGroupingPickerProps,
|
|
43
|
+
): ReactElement {
|
|
44
|
+
const { options, onChange, copy, groupings, label } = props;
|
|
45
|
+
const choices = projectionColourChoices(groupings, copy.bar.uncoloured);
|
|
46
|
+
const Picker =
|
|
47
|
+
choices.length > MOST_SEGMENTS ? OverlaySelect : OverlaySegmented;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Picker
|
|
51
|
+
label={label}
|
|
52
|
+
help={copy.help.colorBy}
|
|
53
|
+
value={options.colorBy}
|
|
54
|
+
disabled={groupings.length === 0}
|
|
55
|
+
options={choices}
|
|
56
|
+
onChange={(colorBy: string) =>
|
|
57
|
+
onChange(projectionColourPatch(options, colorBy))
|
|
58
|
+
}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* What the shape stands for. It is always a list, so a grouping that cannot be
|
|
65
|
+
* shaped keeps its name, greyed, with the reason on the pointer.
|
|
66
|
+
* @param props - See {@link ProjectionShapePickerProps}.
|
|
67
|
+
* @returns The picker, greyed while the samples carry no grouping.
|
|
68
|
+
*/
|
|
69
|
+
export function ProjectionShapePicker(
|
|
70
|
+
props: ProjectionShapePickerProps,
|
|
71
|
+
): ReactElement {
|
|
72
|
+
const { options, onChange, copy, groupings, label, rows } = props;
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<OverlaySelect
|
|
76
|
+
label={label}
|
|
77
|
+
help={copy.help.shapeBy}
|
|
78
|
+
value={options.shapeBy}
|
|
79
|
+
disabled={groupings.length === 0}
|
|
80
|
+
options={projectionShapeChoices(groupings, options, rows, copy)}
|
|
81
|
+
onChange={(shapeBy) => onChange({ shapeBy })}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* How many choices a row of segments holds before it is written as a list: a
|
|
88
|
+
* segment carries a grouping's whole name, and past three they no longer fit
|
|
89
|
+
* the width of a panel.
|
|
90
|
+
*/
|
|
91
|
+
const MOST_SEGMENTS = 3;
|
|
@@ -2,16 +2,15 @@ import type { ReactElement } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import type { OverlayTier } from '../../overlay/core/overlayTiers.ts';
|
|
4
4
|
import { OverlayChip } from '../../overlay/ui/OverlayChip.tsx';
|
|
5
|
-
import { OverlaySegmented } from '../../overlay/ui/OverlaySegmented.tsx';
|
|
6
5
|
import { OverlaySelect } from '../../overlay/ui/OverlaySelect.tsx';
|
|
7
6
|
import { OverlayToggle } from '../../overlay/ui/OverlayToggle.tsx';
|
|
8
7
|
import { OverlayValueMenu } from '../../overlay/ui/OverlayValueMenu.tsx';
|
|
9
8
|
import type { ProjectionCopy } from '../core/projectionCopy.ts';
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from '../core/projectionOptions.ts';
|
|
9
|
+
import type { ProjectionOptions } from '../core/projectionOptions.ts';
|
|
10
|
+
import { NO_GROUPING } from '../core/projectionOptions.ts';
|
|
11
|
+
import type { ProjectionGrouping } from '../core/projectionSamples.ts';
|
|
14
12
|
|
|
13
|
+
import { ProjectionColourPicker } from './ProjectionGroupingPickers.tsx';
|
|
15
14
|
import type { ProjectionReading } from './projectionBarReadings.ts';
|
|
16
15
|
import { projectionChipSettings } from './projectionBarReadings.ts';
|
|
17
16
|
import {
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
} from './projectionEllipse.ts';
|
|
22
21
|
import {
|
|
23
22
|
projectionColourChoices,
|
|
23
|
+
projectionColourPatch,
|
|
24
24
|
projectionOutlineChoices,
|
|
25
25
|
} from './projectionMapChoices.ts';
|
|
26
26
|
|
|
@@ -32,15 +32,13 @@ export interface ProjectionMapControlsProps {
|
|
|
32
32
|
onChange: (patch: Partial<ProjectionOptions>) => void;
|
|
33
33
|
/** The words the bar writes, already merged over the defaults. */
|
|
34
34
|
copy: ProjectionCopy;
|
|
35
|
-
/** What the set of groups is called, which is what `Colour by` offers. */
|
|
36
|
-
groupLabel: string;
|
|
37
35
|
/**
|
|
38
|
-
*
|
|
39
|
-
* outlines stand for nothing, so their
|
|
40
|
-
* removed: a reader who cannot find a
|
|
41
|
-
*
|
|
36
|
+
* Every grouping the samples carry, which is what `Colour by` offers.
|
|
37
|
+
* Without one the colour and the outlines stand for nothing, so their
|
|
38
|
+
* controls are greyed rather than removed: a reader who cannot find a
|
|
39
|
+
* control assumes the figure has none.
|
|
42
40
|
*/
|
|
43
|
-
|
|
41
|
+
groupings: readonly ProjectionGrouping[];
|
|
44
42
|
/**
|
|
45
43
|
* What the two settings currently read, with the figure's own colours. They
|
|
46
44
|
* are built once for the bar and handed down rather than spelled again here,
|
|
@@ -88,18 +86,18 @@ export interface ProjectionMapControlsProps {
|
|
|
88
86
|
export function ProjectionMapControls(
|
|
89
87
|
props: ProjectionMapControlsProps,
|
|
90
88
|
): ReactElement {
|
|
91
|
-
const { options, onChange, copy,
|
|
89
|
+
const { options, onChange, copy, groupings } = props;
|
|
92
90
|
const { readings = NO_READINGS, tier = 'full' } = props;
|
|
93
91
|
const { bar, help, reason } = copy;
|
|
94
92
|
|
|
95
|
-
const
|
|
93
|
+
const hasGroups = groupings.length > 0;
|
|
94
|
+
const uncoloured = !hasGroups || options.colorBy === NO_GROUPING;
|
|
96
95
|
const settings = (
|
|
97
96
|
<MapSettings
|
|
98
97
|
options={options}
|
|
99
98
|
onChange={onChange}
|
|
100
99
|
copy={copy}
|
|
101
|
-
|
|
102
|
-
hasGroups={hasGroups}
|
|
100
|
+
groupings={groupings}
|
|
103
101
|
/>
|
|
104
102
|
);
|
|
105
103
|
|
|
@@ -142,7 +140,7 @@ export function ProjectionMapControls(
|
|
|
142
140
|
|
|
143
141
|
return (
|
|
144
142
|
<>
|
|
145
|
-
<OverlayValueMenu
|
|
143
|
+
<OverlayValueMenu
|
|
146
144
|
label={help.colorBy.title}
|
|
147
145
|
keyWord={bar.key.colorBy}
|
|
148
146
|
showKey={tier === 'full'}
|
|
@@ -150,8 +148,10 @@ export function ProjectionMapControls(
|
|
|
150
148
|
swatches={swatchesOf(readings)}
|
|
151
149
|
disabled={!hasGroups}
|
|
152
150
|
disabledReason={reason.noGroups}
|
|
153
|
-
options={projectionColourChoices(
|
|
154
|
-
onChange={(colorBy) =>
|
|
151
|
+
options={projectionColourChoices(groupings, bar.uncoloured)}
|
|
152
|
+
onChange={(colorBy) =>
|
|
153
|
+
onChange(projectionColourPatch(options, colorBy))
|
|
154
|
+
}
|
|
155
155
|
/>
|
|
156
156
|
<OverlayValueMenu
|
|
157
157
|
label={help.ellipse.title}
|
|
@@ -191,10 +191,8 @@ interface MapSettingsProps {
|
|
|
191
191
|
onChange: (patch: Partial<ProjectionOptions>) => void;
|
|
192
192
|
/** The words the panel writes. */
|
|
193
193
|
copy: ProjectionCopy;
|
|
194
|
-
/**
|
|
195
|
-
|
|
196
|
-
/** Whether the samples carry groups at all. */
|
|
197
|
-
hasGroups: boolean;
|
|
194
|
+
/** Every grouping the samples carry. */
|
|
195
|
+
groupings: readonly ProjectionGrouping[];
|
|
198
196
|
}
|
|
199
197
|
|
|
200
198
|
/**
|
|
@@ -207,24 +205,23 @@ interface MapSettingsProps {
|
|
|
207
205
|
* @returns The captioned controls.
|
|
208
206
|
*/
|
|
209
207
|
function MapSettings(props: MapSettingsProps): ReactElement {
|
|
210
|
-
const { options, onChange, copy,
|
|
211
|
-
const {
|
|
208
|
+
const { options, onChange, copy, groupings } = props;
|
|
209
|
+
const { help, outline } = copy;
|
|
212
210
|
|
|
213
211
|
return (
|
|
214
212
|
<>
|
|
215
|
-
<
|
|
213
|
+
<ProjectionColourPicker
|
|
216
214
|
label={help.colorBy.title}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
onChange={(colorBy) => onChange({ colorBy })}
|
|
215
|
+
options={options}
|
|
216
|
+
onChange={onChange}
|
|
217
|
+
copy={copy}
|
|
218
|
+
groupings={groupings}
|
|
222
219
|
/>
|
|
223
220
|
<OverlaySelect
|
|
224
221
|
label={help.ellipse.title}
|
|
225
222
|
help={help.ellipse}
|
|
226
223
|
value={ellipseKey(options.ellipse)}
|
|
227
|
-
disabled={
|
|
224
|
+
disabled={groupings.length === 0 || options.colorBy === NO_GROUPING}
|
|
228
225
|
options={ellipseChoices(options.ellipse, outline)}
|
|
229
226
|
onChange={(value) => onChange({ ellipse: ellipseSize(value) })}
|
|
230
227
|
/>
|
|
@@ -3,8 +3,8 @@ import type { ReactElement } from 'react';
|
|
|
3
3
|
import type { OverlayLegendEntry } from '../../overlay/ui/OverlayLegend.tsx';
|
|
4
4
|
import { OverlayLegend } from '../../overlay/ui/OverlayLegend.tsx';
|
|
5
5
|
|
|
6
|
+
import { projectionMapCorner } from './projectionMapCorner.ts';
|
|
6
7
|
import type { ProjectionMapCloud } from './projectionMapModel.ts';
|
|
7
|
-
import { projectionMapCorner } from './projectionMapModel.ts';
|
|
8
8
|
import {
|
|
9
9
|
PROJECTION_PLOT_AREA_STYLE,
|
|
10
10
|
PROJECTION_PLOT_ROOM,
|